# encoding  :  utf-8 _*_
# @author   :  朱清闯
# @software :  PyCharm
# time      :  2022/3/26 22:03
from question.serializers import SubjectSerializer, CourseSerializer, ChapterSerializer, SectionSerializer
from question.serializers import KnowledgePointSerializer, QuestionDeserializer, QuestionMultiSerializer, \
    QuestionSerializer
from question.serializers import WrongQuestionModelSerializer, KnowledgePointContainQuestionSerializer, \
    SectionContainQuestionSerializer
from question.serializers import ChapterContainQuestionSerializer, ImageOfQuestionModelSerializer, \
    JointPaperApplicationModelSerializer
from question.serializers import GeneralPaperApplicationModelSerializer, ExaminationPaperModelSerializer, \
    ExaminationPaperModelDeSerializer
from question.serializers import Docx2QuestionModelSerializer, UserSimpleModelSerializer, \
    QuestionFeedbackModelSerializer, Tag2QuestionModelSerializer
from question.serializers import QuestionFeedbackModelDeSerializer, GuidePaperFeedbackModelSerializer, \
    GuidePaperFeedbackModelDeSerializer, DownloadExaminationPaperLogModelSerializer
from question.serializers import FeedbackModelSerializer, FeedbackModelDeSerializer, QuestionNoFavoritesSerializer
from question.serializers import QuestionOnlineApplicationModelSerializer, GuidePaperSerializer, \
    GuidePaperApplicationModelSerializer, UserPlayVideoLogModelSerializer
from question.serializers import CourseContainGuidePaperSerializer, ChallengePaperOfUserModelSerializer
from question.serializers import SwiperImageModelSerializer, BulletinModelSerializer, CategoryModelSerializer, \
    BookModelSerializer, UserDoQuestionLogModelSerializer, PermitVideoOfQuestionModelSerializer, \
    KnowledgePoint2QuestionSerializer, KnowledgePointSimpleSerializer, TagSerializer, QuestionPrivateSerializer, \
    NodeSerializer, NodeCooperationSerializer, Node2QuestionSerializer, QuestionPrivateOnlineApplicationModelSerializer
from question.serializers import PermitExplainPaperOfUserModelSerializer, VideoModelSerializer
from question.serializers import SeatworkModelSerializer, SetWorkModelSerializer, WorkRecordModelSerializer, \
    WorkRecordItemModelSerializer, SetWorkSimpleModelSerializer, SetWorkForStudentModelSerializer
from question.models import QuestionOfPaper, Subject, Course, Chapter, Section, KnowledgePoint, Questions, WrongQuestion
from question.models import ImageOfQuestion, ExaminationPaper, JointPaperApplication, GeneralPaperApplication
from question.models import Feedback, QuestionFeedback, GuidePaperFeedback, ChallengePaperOfUser, \
    QuestionOnlineApplication, Video, PermitVideoOfQuestion, UserPlayVideoLog, Tag2Question
from question.models import GuidePaper, GuidePaperApplication, DownloadQuestionLog, DownloadGuidePaperLog, SwiperImage
from question.models import Bulletin, Category, Book, PermitExplainPaperOfUser, UserDoQuestionLog, \
    DownloadExaminationPaperLog, Question, Tag, Node, NodeCooperation, Node2Question, QuestionPrivateOnlineApplication
from question.models import Seatwork, SetWork, WorkRecord, WorkRecordItem
from account.models import Company, User, UserOfClass, ClassGrade
from utils import ReadDoc2List, SaveImage, BaiduOcr
from utils.myViewSet import CustomModelViewSet
from utils.myViewSet import CustomNoQuestionsModelViewSet
from utils.ReadDocx2QuestionListMarked import get_docx_text, get_new_name
from utils.ReadMarkdown2QuestionListMarked import read_markdown_file_detach, read_markdown_file_attach
from utils import ReadDocx2QuestionListMarked
from rest_framework_bulk.generics import BulkModelViewSet
from rest_framework.response import Response
from utils.response_result import error_result
from utils.MyPage import MyPage, SmallPage, BigPage, MiniPage
from utils.ConvertEnter2Br import convert_enter_2_br
from utils.WriteQuestionList2Docx import write_question_list_into_docx
from utils.WriteQuestionList2Tex import write_questions_2_tex
from utils.WriteQuestionList2Markdown import write_questions_2_markdown
from utils.WriteQuestionList2Latex import write_question_list_into_latex
from utils.WriteQuestionList2Html import write_questions_2_html
from utils.FaceDetection_Tecent import face_detection
from utils.ChatCompletion import chat_completion
from utils import AddQuestionScaleForImage
from utils.SettleBankPointAndDownloadWithQuestionList import settle_bank_point_and_download_question
from utils.TokenQINIU import get_token
import os
from django.conf import settings
from rest_framework.views import APIView
from utils.Pdf2Word_duhui import pdf2word_step1, pdf2word_step2
from utils.Pdf2OtherMiner import mineru
from django.db.models import Q
from rest_framework.decorators import action
import copy
from datetime import date, timedelta, datetime
from django.http import HttpResponse, JsonResponse
import time
import base64
import requests
from celery_tasks.bankpoint.tasks import create_customer_bank_point_log_change
from celery_tasks.download.tasks import create_download_question_log, create_download_guide_paper_log
from celery_tasks.download.tasks import create_download_examination_paper_log
from celery_tasks.notification.tasks import create_send_notification
from celery_tasks.question.tasks import create_user_do_question_log
from django.db import transaction
from utils.auth import Authentication
from utils.Mypermission import IsOwnerPermission, IsAdmin
from utils.CaoliaoQRcode import get_qrcode_and_store
import utils.LatexToOxml as latex_2_word
import utils.Doc2X as Doc2X
from utils import RecognizeEduPaperCutAlibaba, RecognizeEduPaperCutBaidu, FormulaOcrBaidu, FormulaOcrAlibaba
from utils import Pdf2File
import re
from PIL import Image
from django.core.cache import cache
import shutil
from typing import List

data_error_text = "数据格式不正确"
data_success_text = "恭喜，修改成功！"
data_query_success_text = "恭喜，查询成功！"
data_no_need_text = "此操作没必要"
data_upload_ok_online_fail_text = "上传成功, 申请上线失败"
data_ok = '操作成功！'
data_parameters_error = "查询参数错误"
time_format = '%Y-%m-%d'
read_paper_fail = '读取试卷题目失败'
data_refuse = "管理员已经拒绝你的申请，请联系管理员"
apply_ok_result = '恭喜，申请成功！'
form_no_exit = "申请的表单不存在"
data_query_fail = "查询没有记录"
file_upload_ok = '文件上传成功，等待转换'
income_rate = 0.8
price_do_exercise_per_question = 0.5
price_play_video = 2


def write_base64_image_to_file(base64_str, folder, filename):
    try:
        # 确保文件夹存在，如果不存在则创建它
        if not os.path.exists(folder):
            os.makedirs(folder)
        base64_str = base64_str.replace("data:image/png;base64,", '')
        base64_str_bytes = base64_str.encode('utf-8')

        # 对Base64字符串进行解码
        image_data = base64.b64decode(base64_str_bytes)

        # 拼接完整的文件路径
        file_path = os.path.join(folder, filename)

        # 以二进制写入模式打开文件并写入解码后的数据
        with open(file_path, 'wb') as f:
            f.write(image_data)
        print(f"图片已成功写入到 {file_path}")
        return True
    except Exception as e:
        print(f"写入图片时出错: {e}")
        return False


# 删除目录下的所有文件
def delete_all_files_in_folder(folder_path):
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            file_path = os.path.join(root, file)
            try:
                if os.path.isfile(file_path):
                    os.remove(file_path)
            except Exception as e:
                print(f"删除文件 {file_path} 时出错: {e}")


# 复制文件后并删除
def copy_and_delete(source_file, destination_folder):
    try:
        shutil.copy2(source_file, destination_folder)
        os.remove(source_file)
        print(f"文件 {source_file} 已成功复制到 {destination_folder} 文件夹中，并从原位置删除。")
    except Exception as e:
        print(f"操作过程中出错: {e}")


# 提取图片路径，返回值的提取的数组
def extract_image_paths(string):
    pattern = r'static.*?(?:jpg|png|jpeg)'
    return re.findall(pattern, string)


# 判断是否为指定的关键词
def check_keyword(key: str, key_word_list: list) -> str:
    for keyword in key_word_list:
        if key.startswith(keyword):
            return keyword
    return ''


# 从字符串中提取图片标签的内容，并在原字符串中删除图片标签内容。
def extract_image_content(content):
    start_tag = "<img"
    end_tag = "/>"
    start_index = content.find(start_tag)
    end_index = content.find(end_tag)
    if start_index != -1 and end_index != -1:
        str_image = content[start_index:end_index + len(end_tag)]
        new_content = content[:start_index] + content[end_index + len(end_tag):]
        return str_image, new_content
    else:
        return '', content


# 寻找合适的$位置，并返回切割后的结果为数组
def look_up_dollar_position(content: str) -> List[str]:
    position = len(content) // 2
    count = content[:position].count('$')
    if count % 2 == 0:
        return [content[:position]] + [content[position:]]
    elif '$' in content[position - 8:position + 8]:
        pos = position
        exist_flag = False
        while pos > position - 8:
            if content[pos] == '$':
                return [content[:pos]] + [content[pos:]]
            pos += 1
        pos = position
        while pos < position + 8 and not exist_flag:
            if content[pos] == '$':
                return [content[:pos]] + [content[pos:]]
            pos += 1
        return [content]


# 从字符串中提取表格标签的内容，并在原字符串中删除表格标签内容。
def extract_table_content(content):
    start_tag = "<table"
    end_tag = "</table>"
    start_index = content.find(start_tag)
    end_index = content.find(end_tag)
    if start_index != -1 and end_index != -1:
        table_str = content[start_index:end_index + len(end_tag)]
        new_content = content[:start_index] + content[end_index + len(end_tag):]
        return table_str, new_content
    else:
        return '', content


# 两个集合的交集
def intersection(list1, list2):
    set1 = set(list1)
    set2 = set(list2)
    intersection_set = set1 & set2
    return sorted(list(intersection_set))  # 转换为列表并排序


def convert_dic_list(dict_list, key='id'):
    # 使用列表推导式提取每个字典中'id'的值
    return [d[key] for d in dict_list]


class SubjectViewSet(CustomNoQuestionsModelViewSet):
    queryset = Subject.objects.all()
    serializer_class = SubjectSerializer
    permission_classes = []
    authentication_classes = []

    def list(self, request, *args, **kwargs):
        try:
            obj_list = cache.get('subjectList')
            if obj_list:
                return Response(error_result(code=200, results=obj_list, msg='ok'))
            obj_list = self.get_queryset()
            serializer = self.get_serializer(obj_list, many=True)
            cache.set('subjectList', serializer.data, 30 * 24 * 60 * 60)
            return Response(error_result(code=200, results=serializer.data, msg='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class CourseViewSet(CustomNoQuestionsModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    permission_classes = []
    authentication_classes = []

    def get_queryset(self):
        queryset = super(CourseViewSet, self).get_queryset()
        subject = self.request.query_params.get('subject')
        if subject:
            queryset = queryset.filter(subject=subject)
        return queryset

    def list(self, request, *args, **kwargs):
        try:
            obj_list = cache.get('courseList')
            if obj_list:
                return Response(error_result(code=200, results=obj_list, msg='ok'))
            obj_list = self.get_queryset()
            serializer = self.get_serializer(obj_list, many=True)
            cache.set('courseList', serializer.data, 30 * 24 * 60 * 60)
            return Response(error_result(code=200, results=serializer.data, msg='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class ChapterViewSet(CustomNoQuestionsModelViewSet):
    queryset = Chapter.objects.all()
    serializer_class = ChapterSerializer
    permission_classes = []
    authentication_classes = []

    def get_queryset(self):
        queryset = super(ChapterViewSet, self).get_queryset()
        course = self.request.query_params.get('course')
        if course:
            queryset = queryset.filter(course=course)
        return queryset


class SectionViewSet(CustomNoQuestionsModelViewSet):
    queryset = Section.objects.all()
    serializer_class = SectionSerializer
    permission_classes = []
    authentication_classes = []

    def get_queryset(self):
        queryset = super(SectionViewSet, self).get_queryset()
        chapter = self.request.query_params.get('chapter')
        if chapter:
            queryset = queryset.filter(chapter=chapter)
        return queryset


class KnowledgePointViewSet(CustomNoQuestionsModelViewSet):
    queryset = KnowledgePoint.objects.all()
    serializer_class = KnowledgePointSerializer
    pagination_class = MyPage
    permission_classes = []
    authentication_classes = []

    def get_queryset(self):
        queryset = super(KnowledgePointViewSet, self).get_queryset()
        section = self.request.query_params.get('section')
        if section:
            queryset = queryset.filter(section=section)
        return queryset

    def list(self, request, *args, **kwargs):
        try:
            size = request.query_params.get('size')
            subject = request.query_params.get('subject')
            if subject and int(subject) > 0:
                subject = int(subject)
                server_obj_list = KnowledgePoint.objects.filter(subject__pk=subject)
            else:
                server_obj_list = self.get_queryset()

            if size and int(size) > 0:
                page = self.paginate_queryset(server_obj_list)
                serializer = self.get_serializer(page, many=True)
            else:
                serializer = self.get_serializer(server_obj_list, many=True)
            if not serializer:
                return Response(error_result(code=1099, results='no', total=0))
            # print('返回知识点目录：', serializer.data)
            return Response(error_result(code=200, results=serializer.data, total=len(server_obj_list)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    @action(methods=['get'], detail=False)
    def get_knowledge_by_subject(self, request):
        """筛选某学科下的知识点，用于小程序"""
        subject_id = request.query_params['subject_id']
        # print('参数subject_id:', subject_id)
        if subject_id:
            obj_list = KnowledgePoint.objects.filter(subject__id=subject_id)
            if not obj_list:
                obj_list = KnowledgePoint.objects.all()
        else:
            obj_list = KnowledgePoint.objects.all()
        serializer = KnowledgePointSimpleSerializer(obj_list, many=True)
        print('该学科下的知识点：', serializer.data)
        return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))

    @action(methods=['get'], detail=False)
    def get_knowledge_all_subject(self, request):
        """所有学科的知识点"，返回以学科为第一级，该学科的知识点列表为第二级（children）的两层嵌套列表"""
        subject_list_server = Subject.objects.all()
        two_layer_result = [{'text': '', 'value': '', 'children': []} for i in range(len(subject_list_server))]
        if not subject_list_server:
            return Response(error_result(code=1099))
        all_has_done_questions = UserDoQuestionLog.objects.all().values('question')
        all_has_done_questions_ids = convert_dic_list(dict_list=all_has_done_questions, key='question')
        for i in range(len(subject_list_server)):
            subject = subject_list_server[i]
            knowledge_list = subject.knowledgepoints.values('id', 'name')
            children = []
            for knowledge in knowledge_list:
                this_knowledge_questions = knowledge.questions
                this_knowledge_questions_ids = convert_dic_list(dict_list=this_knowledge_questions.values('id'))
                intersection_list = intersection(all_has_done_questions_ids, this_knowledge_questions_ids)
                # has_done_rate_str = '已做' + str(len(intersection_list)) + '题，共' +\
                #                     str(len(this_knowledge_questions))
                temp_dic = {
                    'text': knowledge['name'],
                    'value': knowledge['id'],
                    'has_done': len(intersection_list),
                    'questions_amount': len(this_knowledge_questions_ids)
                }
                children.append(temp_dic)
            two_layer_result[i]['text'] = subject.name
            two_layer_result[i]['value'] = subject.id - 3
            two_layer_result[i]['children'] = children
        return Response(error_result(code=200, results=two_layer_result, total=len(two_layer_result)))

    # 专门用于上传导学案的二级嵌套数组：学科->知识点
    @action(methods=['get'], detail=False)
    def get_knowledge_all_subject_for_upload_guide_paper(self, request):
        """所有学科的知识点"，返回以学科为第一级，该学科的知识点列表为第二级（children）的两层嵌套列表"""
        subject_list_server = Subject.objects.all()
        two_layer_result = [{'text': '', 'value': '', 'children': []} for i in range(len(subject_list_server))]
        for i in range(len(subject_list_server)):
            subject = subject_list_server[i]
            knowledge_list = subject.knowledgepoints.values('id', 'name')
            children = []
            for knowledge in knowledge_list:
                temp_dic = {
                    'text': knowledge['name'],
                    'value': knowledge['id'],
                }
                children.append(temp_dic)
            two_layer_result[i]['text'] = subject.name
            two_layer_result[i]['value'] = subject.id - 3
            two_layer_result[i]['children'] = children
        return Response(error_result(code=200, results=two_layer_result, total=len(two_layer_result)))

    @action(methods=['get'], detail=False)
    def get_knowledge_all_subject_and_course_for_compose(self, request):
        """vue-所有学科的知识点"，三层嵌套数组：该学科、课程、知识点"""
        subject_id = request.query_params.get('subject_id')
        three_layer_result = cache.get('three_layer_result' + str(subject_id))
        if three_layer_result:
            return Response(error_result(code=200, results=three_layer_result, total=len(three_layer_result)))
        three_layer_result = []
        course_list = Course.objects.filter(subject__id=subject_id)
        if len(course_list) == 0:
            return Response(error_result(code=1099))
        course_list_id = convert_dic_list(course_list.values('id'))
        for course_item in course_list:
            course_dic = {'name': course_item.name, 'id': course_item.id, 'children': []}
            chapter_list_id = convert_dic_list(Chapter.objects.filter(Q(course__id=course_item.id)).values('id'))
            section_list_id = convert_dic_list(Section.objects.filter(Q(chapter__id__in=chapter_list_id)).values('id'))
            knowledgepoint_list = KnowledgePoint.objects.filter((Q(section__id__in=section_list_id)))
            for knowledgepoint_item in knowledgepoint_list:
                knowledgepoint_dic = {
                    'name': knowledgepoint_item.name,
                    'id': knowledgepoint_item.id,
                }
                course_dic['children'].append(knowledgepoint_dic)
            three_layer_result.append(course_dic)
        if len(three_layer_result) > 0:
            cache.set('three_layer_result' + str(subject_id), three_layer_result, 10 * 24 * 60 * 60)
            return Response(error_result(code=200, results=three_layer_result, total=len(three_layer_result)))
        else:
            return Response(error_result(code=1099))

    @action(methods=['get'], detail=False)
    def get_knowledge_all_subject_and_course(self, request):
        """小程序-所有学科的知识点"，三层嵌套数组：该学科、课程、知识点，返回该用户的已做题目、知识点名称"""
        subject_list_server = Subject.objects.all()
        three_layer_result = []
        if not subject_list_server:
            return Response(error_result(code=1099))
        all_has_done_questions = UserDoQuestionLog.objects.filter(user__id=request.user.id).values('question')
        all_has_done_questions_ids = convert_dic_list(dict_list=all_has_done_questions, key='question')
        for subject_item in subject_list_server:
            subject_dic = {'text': subject_item.name, 'value': subject_item.id, 'children': []}
            course_list = Course.objects.filter(subject__id=subject_item.id)
            course_list_id = convert_dic_list(course_list.values('id'))
            for course_item in course_list:
                course_dic = {'text': course_item.name, 'value': course_item.id, 'children': []}
                chapter_list_id = list(Chapter.objects.filter(Q(course__id__in=course_list_id)).values('id'))
                section_list_id = list(Section.objects.filter(Q(chapter__id__in=chapter_list_id)))
                knowledgepoint_list = KnowledgePoint.objects.filter((Q(section__id__in=section_list_id)))
                for knowledgepoint_item in knowledgepoint_list:
                    this_knowledge_questions = knowledgepoint_item.questions
                    this_knowledge_questions_ids = convert_dic_list(dict_list=this_knowledge_questions.values('id'))
                    intersection_list = intersection(all_has_done_questions_ids, this_knowledge_questions_ids)
                    has_done_rate = int(len(intersection_list) / len(this_knowledge_questions) * 100)
                    has_done_rate_str = str(has_done_rate) + '%'
                    knowledgepoint_dic = {
                        'rate': has_done_rate_str,
                        'text': knowledgepoint_item.name,
                        'value': knowledgepoint_item.id,
                    }
                    course_dic['children'].append(knowledgepoint_dic)
                subject_dic['children'].append(course_dic)
            three_layer_result.append(subject_dic)
        return Response(error_result(code=200, results=three_layer_result, total=len(three_layer_result)))

    # 某个学科下的练习知识点-小程序， 定制字典key的value, text等字段，为了①直接适配前端的卡片显示,②减少网络开销
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_knowledge_by_subject_id_for_exercise(self, request):
        try:
            subject_id = request.query_params.get('subject_id')
            if not subject_id:
                subject_id = 1
            knowledgepoint_list_server = KnowledgePoint.objects.filter(subject__id=subject_id)
            all_has_done_questions = UserDoQuestionLog.objects.filter(user__id=request.user.id)
            # print('all_has_done_questions', all_has_done_questions)
            all_has_done_questions_ids = []
            if all_has_done_questions is not None:
                all_has_done_questions = all_has_done_questions.values('question')
                all_has_done_questions_ids = convert_dic_list(dict_list=all_has_done_questions, key='question')
            knowledge_list_dic = []
            for knowledgepoint_item in knowledgepoint_list_server:
                # this_knowledge_questions = knowledgepoint_item.questions
                this_knowledge_questions = Questions.objects.filter(Q(knowledgepoint__id=knowledgepoint_item.id) &
                                                                    Q(online=True) &
                                                                    Q(open_level='public'))
                if this_knowledge_questions:
                    this_knowledge_questions_ids = convert_dic_list(dict_list=this_knowledge_questions.values('id'))
                    intersection_list = intersection(all_has_done_questions_ids, this_knowledge_questions_ids)
                    # print('all_has_done_questions_ids', all_has_done_questions_ids)
                    # print('this_knowledge_questions_ids', this_knowledge_questions_ids)
                    has_done_rate = int(len(intersection_list) / len(this_knowledge_questions) * 100)
                    has_done_rate_str = str(has_done_rate) + '%'
                    knowledgepoint_dic = {
                        'has_done': len(intersection_list),
                        'questions_amount': len(this_knowledge_questions),
                        'rate': has_done_rate_str,
                        'name': knowledgepoint_item.name,
                        'id': knowledgepoint_item.id,
                    }
                else:
                    knowledgepoint_dic = {
                        'has_done': 0,
                        'questions_amount': 0,
                        'rate': '0%',
                        'name': knowledgepoint_item.name,
                        'id': knowledgepoint_item.id,
                    }
                knowledge_list_dic.append(knowledgepoint_dic)
            return Response(error_result(code=200, results=knowledge_list_dic, total=len(knowledge_list_dic)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class QuestionMultiViewSet(BulkModelViewSet, CustomModelViewSet):
    """
    create: 创建多个题目，参数为多个题目作为字典的列表。比如[{"id":1,"content":"以下说法正确的是：","answer":"A"……}]
    """
    queryset = Questions.objects.all()
    serializer_classes = {
        'list': QuestionDeserializer,
        'retrieve': QuestionDeserializer,
        'create': QuestionMultiSerializer,
        'update': QuestionSerializer,
    }
    default_serializer_class = QuestionMultiSerializer
    filter_fields = ('type', 'difficulty_level', 'owner')
    permission_classes = []
    authentication_classes = []

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_classes.get(self.action, self.default_serializer_class)


def select_queryset(key, query_value, year, question_type):
    if key == 'course':
        return Chapter.objects.filter(course__id=query_value)
    elif key == 'chapter':
        return Section.objects.filter(chapter__id=query_value)
    elif key == 'section':
        return KnowledgePoint.objects.filter(section__id=query_value)
    elif key == 'knowledgepoint':
        # return Questions.objects.filter(knowledgepoint__id=query_value)
        # return Questions.objects.filter(knowledgepoint__id=query_value)
        if year != '' and year != 'all' and (question_type == '' or question_type == 'all'):
            return KnowledgePoint.objects.get(id=query_value).question.filter(publish_time__year=int(year))
        elif year != '' and year != 'all' and question_type != '' and question_type != 'all':
            return KnowledgePoint.objects.get(id=query_value).question.filter(Q(publish_time__year=int(year)) &
                                                                              Q(type=int(question_type)))
        elif (year == '' or year == 'all') and question_type != '' and question_type != 'all':
            return KnowledgePoint.objects.get(id=query_value).question.filter(type=int(question_type))
        return KnowledgePoint.objects.get(id=query_value).question.all()


# 该方法已经被弃用，因为去掉前缀http//ip的逻辑交给前端完成了
def discard_prefix(images):
    pattern = r'(\/static\/.+)'
    pictures = ''
    if not images:
        pictures = ''
    elif len(images) == 1:
        pictures = re.search(pattern, images[0]).group(1)
    else:
        temp_pictures = []
        [temp_pictures.append(re.search(pattern, image).group(1)) for image in images]
        pictures = '-'.join(temp_pictures)
    return pictures


# 把字符串转换成标准的html标签
def replace_tags_with_paragraphs(text):
    # 正则表达式，用于匹配<br>标签、换行符和需要特殊处理的HTML标签
    br_or_newline = re.compile(r'<br[^>]*>|\\n')
    block_tags = re.compile(r'<(img|table|[^>]*?script[^>]*?)[^>]*>', re.IGNORECASE)

    # 辅助函数，用于在合适的位置插入<p>或</p>

    def insert_paragraphs(match):
        tag = match.group(0)
        if tag.startswith('<br') or tag == '\n':
            return '</p>'
        elif block_tags.match(tag):
            return '</p>' + tag + '<p>'
        else:
            # 这里假设其他情况不会中断<p>标签
            return tag
            # 初始处理，确保字符串以<p>开头

    if not text.startswith('<p>'):
        text = '<p>' + text
        # 使用正则表达式和辅助函数进行替换
    text = br_or_newline.sub(insert_paragraphs, text)
    # 如果字符串以非<p>标签结尾，则添加</p>
    if not text.endswith('</p>'):
        text = text.rstrip() + '</p>'
        # 移除连续的</p><p>
    text = re.sub(r'</p>\s*<p>', '', text)
    return text


# 为把普通的虎图片路径转换成标准的html的img标签
def add_marked_for_pictures(field_content, pictures, field_name):
    if '-' not in pictures:
        pictures = ReadDocx2QuestionListMarked.img_add_marked(pictures)
        field_content += pictures
        obj_new = {field_name: field_content}
    else:
        pictures = pictures.split('-')
        for picture in pictures:
            picture = ReadDocx2QuestionListMarked.img_add_marked(picture)
            field_content += picture
        obj_new = {field_name: field_content}
    return obj_new


# 增加图片标签的右括号
def add_greater_than(field_content, field_name):
    content_item_list = field_content.split('<img')
    for i in range(len(content_item_list)):
        if 'src' in content_item_list[i][:5]:
            content_item_list[i] = '<img ' + content_item_list[i] + ' >'
    content_new = ''.join(content_item_list)
    obj_new = {field_name: content_new}
    return obj_new


# 在选择题选项A.前添加</P><P>标签
def add_paragraphs_before_A(text):
    # 使用正则表达式查找"A  ."（A和点之间可能有任意数量的空格）
    # 并替换为"A."
    text = re.sub(r'A\s*\.', 'A.', text)
    # 使用正则表达式查找"A."，并在其前面插入"</p><p>"
    # 注意：这里我们假设A.之前不会有其他的HTML标签导致不恰当的嵌套
    text = re.sub(r'(?<!</p>)A\.', '</p><p>A.', text)
    # 如果字符串以A.开始，则需要在前面添加<p>标签
    if text.startswith('A.'):
        text = '<p>' + text
        # 如果字符串不以</p>结束，并且不是以A.单独结尾（即A.后面还有内容），则需要在后面添加</p>
    if not text.endswith('</p>') and not text.endswith('A.'):
        # 使用正则表达式查找最后一个非A.字符后面的位置，并添加</p>
        text = re.sub(r'([^A\.])\Z', r'\g<1></p>', text)
        # 如果字符串以A.单独结尾，我们不需要添加额外的</p>标签
    return text


# 删除p标签前面的多余\n
def remove_newline_before_p_tag(input_string):
    # 使用正则表达式匹配'\n<p>'并将其替换为'<p>'
    import re
    result = re.sub(r'\n+<p>', '<p>', input_string)
    result = re.sub(r'\n+(</p>)', r'\1', result)
    result = re.sub(r'\n+(<img>)', r'\1', result)
    result = re.sub(r'\n+(</img>)', r'\1', result)
    result = re.sub(r'\n+(<table>)', r'\1', result)
    result = re.sub(r'\n+(</table>)', r'\1', result)
    return result


class QuestionViewSet(CustomNoQuestionsModelViewSet):
    queryset = Questions.objects.all()
    queryset_new = Questions
    serializer_class = QuestionSerializer
    filter_fields = ('type', 'difficulty_level', 'owner', 'knowledgepoint')
    pagination_class = SmallPage

    def get_queryset(self):
        queryset = super(QuestionViewSet, self).get_queryset()
        knowledgepoint = self.request.query_params.get('knowledgepoint')
        if knowledgepoint:
            queryset = queryset.filter(knowledgepoint=knowledgepoint)
        return queryset

    def create(self, request, *args, **kwargs):
        question_modify_list = []
        owner = self.request.user
        if isinstance(request.data, dict):
            request.data['owner'] = owner.id
            request.data['online'] = False
            question_modify_list.append(request.data)
        elif isinstance(request.data, list):
            for item in request.data:
                item['owner'] = owner.id
                item['online'] = False
                question_modify_list.append(item)
        question_ser = self.get_serializer(data=question_modify_list, many=True)
        if not question_ser.is_valid():
            return Response(error_result(code=406, msg=question_ser.errors, results=data_error_text))
        else:
            question_ser.save()
            data = {'count': len(question_ser.data), 'results': question_ser.data}
            return Response(error_result(code=200, msg="恭喜，添加成功！", results=data))

    def update(self, request, *args, **kwargs):
        request_data = request.data
        pk = kwargs.get('pk', None)
        modify_data_list = []
        question_list = []
        if pk and isinstance(request_data, dict):
            question_id = request_data.pop("id", None)
            knowledgepoint = request_data.pop("knowledgepoint", None)
            pk = int(pk)
            if pk == question_id:
                request_data['last_edit_time'] = datetime.now()
                request_data['online'] = False
                question_server = Questions.objects.get(pk=pk)
                if len(knowledgepoint) > 0:
                    knowledgepoint_list_id = convert_dic_list(dict_list=knowledgepoint)
                    knowledgepoint_list_server = KnowledgePoint.objects.filter(id__in=knowledgepoint_list_id)
                    question_server.knowledgepoint_set.set(list(knowledgepoint_list_server))
                modify_data_list.append(request_data)
                question_list.append(question_server)
            else:
                return Response(error_result(code=406, msg="id不一致", results="data_error_text"))
        elif not pk and isinstance(request_data, list):
            for item in request_data:
                pk = item.pop("id")
                item['last_edit_time'] = datetime.now()
                item['online'] = False
                question_obj = Questions.objects.get(pk=pk)
                question_list.append(question_obj)
                modify_data_list.append(item)
        else:
            return Response(error_result(code=406, msg="data_error_text", results="data_error_text"))
        question_ser = QuestionSerializer(instance=question_list, data=modify_data_list, many=True, partial=True)
        if not question_ser.is_valid(raise_exception=False):
            return Response(error_result(code=406, msg=question_ser.errors, results=question_ser.errors))
        else:
            question_ser.save()
            data = {'count': len(question_ser.data), 'results': question_ser.data}
            return Response(error_result(code=200, msg=data_success_text, results=data))

    def retrieve(self, request, *args, **kwargs):
        pk = int(kwargs.get('pk'))
        if pk:
            try:
                question_obj = Questions.objects.get(pk=pk)
                question_ser = QuestionSerializer(question_obj)
                return Response(error_result(code=200, msg=data_query_success_text, results=question_ser.data))
            except Exception as E:
                print('Exception:', E)
                return Response(error_result(code=500, msg="查询错误", results='no'))
        else:
            return Response(error_result(code=500, msg="查询错误", results='no'))

    def list(self, request, *args, **kwargs):
        try:
            question_list = Questions.objects.filter(Q(open_level="public") & Q(online=True))
            page = self.paginate_queryset(question_list)
            question_ser = self.get_serializer(page, many=True)
            data = question_ser.data
            return Response(error_result(code=200, msg=data_query_success_text, results=data, total=len(question_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="查询错误", results="查询错误"))

    @action(methods=['get'], detail=False)
    def catalogue(self, request):
        """扁平化题目列表， 思路：先找到当前查询的节点，一直往后查找，把子id连接，直到questionqueryset，获取问题"""
        node = ['course', 'chapter', 'section', 'knowledgepoint']
        query_keys = request.query_params
        year = ''
        question_type = ''
        if 'type' in query_keys:
            question_type = query_keys["type"]
        if 'year' in query_keys:
            year = query_keys["year"]
            # del query_keys["year"]
        father_id_list = children_id_list = []
        parameter_position = -1
        for index, item in enumerate(node):
            # 遍历query_keys字典的键，即安测是否携带哪个目录的范围
            if item in query_keys:
                parameter_position = index
                break
        # 没有携带目录位置
        if parameter_position == -1:
            return Response(error_result(code=1099, msg="查询错误", results="查询错误"))
        query_key = node[parameter_position]
        query_value = query_keys[query_key]
        father_id_list.append(query_value)
        while parameter_position < len(node):
            children_id_list = []
            for father_id in father_id_list:
                query_key = node[parameter_position]
                query_value = father_id
                temp_query = select_queryset(query_key, query_value, year, question_type)
                if temp_query:
                    if parameter_position < len(node) - 1:
                        children_id_list.extend([item.id for item in temp_query])
                    else:
                        children_id_list.extend(item for item in temp_query)
            father_id_list = copy.deepcopy(children_id_list)
            parameter_position += 1
        page = self.paginate_queryset(father_id_list)
        question_ser = self.get_serializer(page, many=True)
        data = question_ser.data
        return Response(error_result(code=200, msg=data_query_success_text, results=data, total=len(father_id_list)))

    # 非试卷的试题
    @action(methods=['get'], detail=False)
    def no_paper_question(self, request):
        """获取所有非试卷的题目，即只有关联知识点的题目列表，并且有owner筛选"""
        owner_id = request.user.id
        question_list = Questions.objects.filter(owner__id=owner_id)
        question_of_paper_list = QuestionOfPaper.objects.all().values("question_id")
        question_list = question_list.exclude(id__in=question_of_paper_list)
        page = self.paginate_queryset(question_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(question_list)))

    # 所有自己录入的试题
    @action(methods=['get'], detail=False)
    def question_owner_all(self, request):
        owner_id = request.user.id
        question_list = Questions.objects.filter(owner__id=owner_id)
        page = self.paginate_queryset(question_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(question_list)))

    # 搜索非试卷试题，自己的
    @action(methods=['get'], detail=False)
    def search_with_owner(self, request):
        """获取所有非试卷的题目，即只有关联知识点的题目列表，并且有owner筛选"""
        owner_id = request.user.id
        keyword = request.query_params['query_key']
        question_list = Questions.objects.filter(Q(owner__id=owner_id) & Q(content__contains=keyword))
        question_of_paper_list = QuestionOfPaper.objects.all().values("question_id")
        question_list = question_list.exclude(id__in=question_of_paper_list)
        if not question_list:
            return Response(error_result(code=1099, results='no', msg='该关键字无内容'))
        page = self.paginate_queryset(question_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(question_list)))

    @action(methods=['get'], detail=False)
    def search_with_owner_all(self, request):
        """获取所有非试卷的题目，即只有关联知识点的题目列表，并且有owner筛选"""
        owner_id = request.user.id
        keyword = request.query_params.get('query_key')
        if keyword is None:
            return Response(error_result(code=1099, results=[], msg='该关键字无内容'))
        question_list = Questions.objects.filter(Q(owner__id=owner_id) & Q(content__contains=keyword))
        if not question_list:
            return Response(error_result(code=1099, results='no', msg='该关键字无内容'))
        page = self.paginate_queryset(question_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(question_list)))

    # 用户设置自己试题的开放程度，不包含试卷的题目
    @action(methods=['patch'], detail=True)
    def setup_open_level(self, request, *args, **kwargs):
        try:
            open_level = request.data['open_level']
            if open_level not in ['private', 'public']:
                return Response(error_result(code=1099, msg="关键词必须是public或private", results='no'))
            obj_pk = kwargs['pk']
            query_obj = Questions.objects.filter(Q(pk=obj_pk) & Q(owner__id=request.user.id))

            if not query_obj:
                return Response(error_result(code=1099, msg="不存在", results='no'))
            else:
                query_obj = query_obj.first()
            if open_level != query_obj.open_level:
                query_obj.open_level = open_level
                query_obj.save()
            else:
                return Response(error_result(code=1022, msg=data_no_need_text, results='No'))
            # 根据时间范围返回，默认缺省天数为所有
            query_days = request.data.get('days', None)

            if not query_days:
                query_obj = Questions.objects.filter(owner__id=request.user.id)
            elif query_days == 0:
                query_obj = Questions.objects.filter(owner__id=request.user.id)
            elif query_days > 0:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                query_obj = Questions.objects.filter(Q(add_date__gt=past_date) & Q(owner__id=request.user.id))
            if not query_obj:
                return Response(error_result(code=1099, msg="不存在", results='no'))
            question_of_paper_list = QuestionOfPaper.objects.all().values("question_id")
            query_obj = query_obj.exclude(id__in=question_of_paper_list)
            page = self.paginate_queryset(query_obj)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(query_obj)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))

    # 用户添加错题记录
    @action(methods=['post'],
            detail=False,
            url_path='create_wrong_question',
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def create_wrong_question(self, request, *args, **kwargs):
        try:
            user = self.request.user
            question_id = request.data['question_id']
            wrong_question_obj = WrongQuestion.objects.filter(Q(question__id=question_id) &
                                                              Q(user__id=user.id))
            if wrong_question_obj:
                return Response(error_result(code=1022, msg='该题已经收藏，无须重复添加', results="no"))
            wrong_question_dic = {'question': question_id, 'user': user.id}
            serializer = WrongQuestionModelSerializer(data=wrong_question_dic)
            if not serializer.is_valid():
                print(serializer.errors)
                return Response(error_result(code=1099, msg='参数错误', results="no"))
            serializer.save()
            wrong_question_list = Questions.objects.filter(wrong__pk=user.id)
            page = request.data.get('page')
            if page and int(page) >= 1:
                page_list = self.paginate_queryset(wrong_question_list)
                serializer = QuestionSerializer(page_list, many=True)
            else:
                serializer = QuestionSerializer(wrong_question_list, many=True)
            return Response(error_result(code=200, msg='增加错题成功', results=serializer.data))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='增加错题失败', results="no"))

    # 用户添加错题,并返回错题的id列表
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def create_wrong_question_return_id_list(self, request, *args, **kwargs):
        try:
            user = self.request.user
            question_id = request.data['question_id']
            wrong_question_obj = WrongQuestion.objects.filter(Q(question__id=question_id) &
                                                              Q(user__id=user.id))
            if wrong_question_obj:
                return Response(error_result(code=1022, msg='该题已经收藏，无须重复添加', results="no"))
            wrong_question_dic = {'question': question_id, 'user': user.id}
            serializer = WrongQuestionModelSerializer(data=wrong_question_dic)
            if not serializer.is_valid():
                print(serializer.errors)
                return Response(error_result(code=1099, msg='收藏失败', results="no"))
            serializer.save()
            wrong_question_list = WrongQuestion.objects.filter(user__id=user.id).values('question')
            wrong_question_list_id = convert_dic_list(dict_list=wrong_question_list, key='question')
            return Response(error_result(code=200, msg='增加错题成功', results=wrong_question_list_id))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='增加错题失败', results="no"))

    # 获取自己名下的错题，返回结果不分页或不分页，根据参数page
    @action(methods=['get'],
            detail=False,
            )
    def get_wrong_no_page_myself(self, request):
        try:
            server_obj_list = WrongQuestion.objects.filter(user__id=request.user.id)
            if not server_obj_list:
                return Response(error_result(code=1099, results=[], msg='没有错题', total=0))
            serializer = WrongQuestionModelSerializer(server_obj_list, many=True)
            if not serializer:
                return Response(error_result(code=1099, results=[], msg='没有错题', total=0))
            return Response(error_result(code=200, results=serializer.data, total=len(server_obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 获取自己名下的错题，返回结果不分页或不分页，根据参数page
    @action(methods=['get'],
            detail=False,
            url_path='get_wrong_for_owner'
            )
    def get_wrong_for_owner(self, request):
        try:
            server_obj_list = WrongQuestion.objects.filter(user__id=request.user.id)
            if not server_obj_list:
                return Response(error_result(code=1099, results='no', total=0))
            page = request.query_params.get('page', None)
            if page and int(page) >= 1:
                page_list = self.paginate_queryset(server_obj_list)
                serializer = WrongQuestionModelSerializer(page_list, many=True)
            else:
                serializer = WrongQuestionModelSerializer(server_obj_list, many=True)
            if not serializer:
                return Response(error_result(code=1099, results='no', total=0))
            return Response(error_result(code=200, results=serializer.data, total=len(server_obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 获取自己名下的所有错题
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def get_all_wrong_question_id_for_owner(self, request):
        try:
            server_obj_list = WrongQuestion.objects.filter(user__id=request.user.id)
            if not server_obj_list:
                return Response(error_result(code=200, results=[], total=0))
            else:
                server_obj_list_id = convert_dic_list(dict_list=server_obj_list.values('question'), key='question')
                # serializer = WrongQuestionModelSerializer(server_obj_list, many=True)
                return Response(error_result(code=200, results=server_obj_list_id, total=len(server_obj_list)))

        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 取消自己的错题收藏，并返回最新的收藏列表（分页或不分页，根据参数page）
    @action(methods=['delete'],
            detail=True,
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def cancel_wrong_for_owner(self, request, pk):
        try:
            user = User.objects.get(pk=request.user.id)
            question_id = request.query_params['question_id']
            question_obj = Questions.objects.get(pk=question_id)
            question_obj.wrong.remove(user)
            question_list = Questions.objects.filter(wrong__user__pk=request.user.id)
            count = len(question_list)
            if count == 0:
                return Response(error_result(code=1099, msg='该用户没有收藏错题', results="", total=0))
            page = request.query_params['page']
            if page and int(page) >= 1:
                page_list = self.paginate_queryset(question_list)
                ser_obj = QuestionSerializer(page_list, many=True).data
            else:
                ser_obj = QuestionSerializer(question_list, many=True).data
            return Response(error_result(code=200, msg='取消错题成功', results=ser_obj, total=count))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='取消错题失败', results="", total=0))

    # 取消自己的错题收藏，并返回最新的收藏列表id）
    @action(methods=['delete'],
            detail=False,
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def cancel_wrong_question_return_id_list(self, request):
        try:
            question_id = request.data.get('question_id')
            if question_id is None:
                return Response(error_result(code=1099, msg='题号是必须要提供的', results=[], total=0))
            wrong_question_list = WrongQuestion.objects.filter(Q(user__id=request.user.id) &
                                                               Q(question__id=question_id))
            if not wrong_question_list:
                return Response(error_result(code=1099, msg='该题未收藏，没必要取消', results=[], total=0))
            wrong_question_obj = wrong_question_list.first()
            wrong_question_obj.delete()
            wrong_question_list_new = WrongQuestion.objects.filter(user__id=request.user.id)
            wrong_question_list_id = convert_dic_list(wrong_question_list_new.values('question'), key='question')
            count = len(wrong_question_list_id)
            return Response(error_result(code=200, msg='取消错题成功', results=wrong_question_list_id, total=count))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='取消错题失败', results=[], total=0))

    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[])
    def search_content_by_keyword(self, request):
        """题干的关键词搜索试题"""
        try:
            keyword = request.query_params['keyword']
            print('keyword:', keyword)
            if len(keyword) == 0:
                return Response(error_result(code=1099, results='no', msg='关键词不能空'))
            question_list = Questions.objects.filter(Q(open_level='public') & Q(online=True) &
                                                     Q(content__contains=keyword))
            if not question_list:
                return Response(error_result(code=1099, results='no', msg='查询为空'))
            page = self.paginate_queryset(question_list)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(question_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='获取默认试题失败', results=[], total=0))

    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[])
    def no_auth(self, request):
        """无须登录，读取所有题目的前20条数据"""
        try:
            question_list = cache.get('no_auth')
            if question_list:
                return Response(error_result(code=200, results=question_list, total=len(question_list)))
            question_list = Questions.objects.filter(Q(open_level='public') & Q(online=True))[:10]
            # print('首页前20条数据为：', question_list)
            if not question_list:
                return Response(error_result(code=1099, results='no', msg='查询为空'))
            serializer = self.get_serializer(question_list, many=True)
            cache.set('no_auth', serializer.data, 10 * 24 * 60 * 60)
            return Response(error_result(code=200, results=serializer.data, total=len(question_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='获取默认试题失败', results=[], total=0))

    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[])
    def no_auth_wx(self, request):
        """无须登录，读取所有题目的前20条数据"""
        try:
            question_list = Questions.objects.filter(Q(open_level='public') & Q(online=True))[:10]
            # print('首页前20条数据为：', question_list)
            if not question_list:
                return Response(error_result(code=1099, results='no', msg='查询为空'))
            table = []
            image = []
            content_result = []
            content_no_image_str = ''
            for i in range(len(question_list)):
                content = question_list[i].content
                # 先分离出所有的表格
                match = re.search(r'<table(.*?)</table', content)
                while match:
                    temp_table, content = extract_table_content(content)
                    if len(temp_table) > 0:
                        table.append(temp_table)
                    match = re.search(r'<table(.*?)</table', content)
                # 再分离出所有图片
                match = re.search(r'<img(.*?)/>', content)
                while match:
                    temp_image, content = extract_image_content(content)
                    if len(temp_image) > 0:
                        image.append(temp_image)
                    match = re.search(r'<img(.*?)/>', content)
                # 去掉<p>标签
                if len(content) == 0:
                    content = '<p>如下图所示：</p>'
                content = content.replace(r'<p>', '')
                content_temp_list = content.split('</p>')
                content_temp_list = content_temp_list[:len(content_result) - 1]
                content_temp_list = content_temp_list[:len(content_result) - 1]
                for item in content_temp_list:
                    print('item:', item)
                    if item.count('$') % 2 == 1:
                        return Response(error_result(code=1099, results='no',
                                                     msg='如果您看到该题目，请报告管理员，该题目存在公式错误'))
                    if len(item) <= 30:
                        content_result += [item]
                        continue
                    count_dollar = 0
                    count_valid = 0
                    keyword_list = [r'{', r'}', r'\left', r'\right', r'\frac', r'\sqrt', r'\geq', r'\underline',
                                    r'\quad', r'matrix', r'\begin', r'\end', r'\bigtriangleup', 'bot', 'mathrm',
                                    r'\angle', r'\tag', r'array', r'\vert']
                    # 每个20字有效字符，则划分出来
                    i = 0
                    while i < len(item):
                        if item[i] == '$':
                            count_dollar += 1
                            if count_valid >= 20 and count_dollar % 2 == 0:
                                content_result += [item[:i + 1]]
                                item = item[i + 1:]
                                content_result += [item[:i + 1]]
                                item = item[i + 1:]
                            i += 1
                        else:
                            is_keyword = check_keyword(item, keyword_list)
                            if len(is_keyword) > 0:
                                i += len(is_keyword)
                            else:
                                count_valid += 1
                                if count_valid >= 20 and count_dollar % 2 == 0:
                                    content_result += [item[:i + 1]]
                                    content_result += [item[:i + 1]]
                                    item = item[i + 1:]
                                    count_valid = 0
                                i += 1
                    if len(item) > 0:
                        content_result += [item]
                    if len(content_result) == 1:
                        content_no_image_str = content_result[0]
                    else:
                        content_no_image_str = r' \newline '.join(content_result)

            serializer = self.get_serializer(question_list, many=True)
            # print('serializer:', serializer.data)
            results = serializer.data
            for i in range(len(results)):
                if len(table) >= 1:
                    results[i]['table_image_content'] = table[0]
                if len(image) >= 1:
                    results[i]['table_image_content'] += image[0]
                results[i]['content_no_table_image'] = content_no_image_str
            return Response(error_result(code=200, results=results, total=len(question_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='获取默认试题失败', results=[], total=0))

    # 筛选单个知识点下的题目列表
    @action(methods=['get'], detail=False)
    def get_question_by_knowledge(self, request):
        """筛选某知识点下的题目"""
        knowledge_id = request.query_params['knowledge_id']
        question_list = KnowledgePoint.objects.get(id=knowledge_id).question.all()
        # question_list = Questions.objects.filter(knowledgepoint__id=knowledge_id)
        if not question_list:
            return Response(error_result(code=1099, results='no', msg='该关键字无内容'))
        page = self.paginate_queryset(question_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(question_list)))

    # 筛选多个知识点下的题目
    @action(methods=['get'], detail=False)
    def get_question_by_knowledge_list(self, request):
        """筛选指定试卷id下，指定用户id为owner下的题目"""
        knowledge_list = list(request.query_params['knowledge_list'])
        if not knowledge_list:
            return Response(error_result(code=1099, results=[]))
        question_list_server = ExaminationPaper.objects.filter(Q(online=True) &
                                                               Q(open_level='public') &
                                                               Q(knowledgepoint__id__in=knowledge_list))
        # print('question_list:', question_list_server)
        if not question_list_server:
            return Response(error_result(code=1099, results=[], msg='该关键字无内容'))
        serializer = self.get_serializer(question_list_server, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(question_list_server)))

    # 筛选多个知识点下的题目，且筛选是否已做题目,has_done为True表示题目列表可以包含已做题要，False表示不要已做题
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_question_filter_knowledge_and_has_done(self, request):
        """筛选指定试卷id下，指定用户id为owner下的题目"""
        try:
            current_user_server = User.objects.get(id=request.user.id)
            knowledge_id = request.query_params.get('knowledgepoint')
            if not knowledge_id:
                return Response(error_result(code=1099, results=[]))
            knowledge_id = int(knowledge_id)
            has_done = request.query_params.get('has_done')
            if has_done and int(has_done) == 1:
                has_done = True
            elif has_done and int(has_done) == 2:
                has_done = False
            elif has_done is None:
                has_done = True
            if has_done:
                question_list_server = Questions.objects.filter(Q(online=True) &
                                                                Q(open_level='public') &
                                                                Q(knowledgepoint__id=knowledge_id))

            else:
                has_done_list_id = convert_dic_list(current_user_server.do_question.values('id'))
                # print('has_done_list_id', has_done_list_id)
                question_list_server = Questions.objects.filter(Q(online=True) &
                                                                Q(open_level='public') &
                                                                Q(knowledgepoint__id=knowledge_id) &
                                                                ~Q(id__in=has_done_list_id))
            # print('question_list:', question_list_server)
            if not question_list_server:
                return Response(error_result(code=1099, results=[], msg='该关键字无内容'))
            serializer = self.get_serializer(question_list_server, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(question_list_server)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选多个知识点下的题目，且筛选是否已做题目,has_done为True表示题目列表可以包含已做题要，False表示不要已做题
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def add_scale(self, request):
        try:
            question_server_list = Questions.objects.all()
            AddQuestionScaleForImage.add_scale(question_server_list)
            return Response(error_result(code=200, msg='ok', results='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 申请解析指定的试题视频解析
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_permit_question_video(self, request, *args, **kwargs):
        try:
            question_id = request.data['question']
            obj_server = PermitVideoOfQuestion.objects.filter(Q(user__id=request.user.id) &
                                                              Q(question__id=question_id))
            if obj_server:
                return Response(error_result(code=1099, msg='你已经申请过，不能重复申请', results='no'))
            new_data_dic = {'user': request.user.id, 'question': question_id, 'status': 0}
            serializer = PermitVideoOfQuestionModelSerializer(data=new_data_dic)
            if not serializer.is_valid(raise_exception=False):
                print("申请解析的序列化错误:", serializer.errors)
                return Response(error_result(code=1099, msg=serializer.errors))
            serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 管理员审批用户的申请试题视频解析, 结果发给申请者通知
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def permit_explain_approval(self, request, *args, **kwargs):
        try:
            question_id = request.data['question']
            user_id = request.data['user']
            status = request.data['status']
            obj_server = PermitVideoOfQuestion.objects.filter(Q(user__id=user_id) & Q(question__id=question_id))
            if not obj_server:
                return Response(error_result(code=1099, msg='申请的用户id或试题id有误', results='no'))
            modify_dic = {'question': question_id, 'user': user_id, 'status': status}
            obj_server = obj_server.first()
            serializer = PermitVideoOfQuestionModelSerializer(instance=obj_server, data=modify_dic, partial=True)
            if not serializer.is_valid():
                return Response(error_result(code=1099, msg=data_error_text, results=serializer.errors))
            serializer.save()
            state_cn = {False: '禁止', True: '批准'}
            create_send_notification.delay(user=user_id,
                                           content='你申请解析的试卷”' + obj_server.question.content[:10] + '”，状态为'
                                                   + state_cn[status])
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 参数applied_type取值范围为[0, 1, 2, 3]，0代表全部，1代表自己申请过未被批准的；2代表未申请的，3代表申请且被批准的, 且未上传视频。
    # 所有筛选都在指定学科下
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication]
            )
    def get_permit_question_video(self, request):
        try:
            applied_type = request.query_params.get('applied_type')
            subject_id = request.query_params.get('subject_id')
            current_date = datetime.now()
            has_permit_question_list = PermitVideoOfQuestion.objects.all().values("question")
            has_permit_question_list_id = convert_dic_list(dict_list=has_permit_question_list, key='question')
            # 处理学科
            if subject_id is None:
                subject_id = 1
            elif int(subject_id) < 1 or int(subject_id) > 3:
                subject_id = 1
            else:
                subject_id = int(subject_id)
            # 获取该学科下的所有试题
            subject_knowledgepoint_list = KnowledgePoint.objects.filter(subject__id=subject_id).values('id')
            subject_knowledgepoint_list_id = convert_dic_list(dict_list=subject_knowledgepoint_list)
            subject_knowledge_question_list = Questions.objects.filter(
                knowledgepoint__id__in=subject_knowledgepoint_list_id).values('id')
            subject_knowledge_question_list_id = convert_dic_list(dict_list=subject_knowledge_question_list)
            if applied_type is None:
                applied_type = 2
                server_obj_list = Questions.objects.filter(~Q(id__in=has_permit_question_list_id) &
                                                           Q(expiration__gte=current_date) &
                                                           Q(need_video=True) &
                                                           Q(id__in=subject_knowledge_question_list_id))
            # 0 左右戴申请的题目列表
            elif int(applied_type) == 0:
                server_obj_list = Questions.objects.filter(Q(expiration__gte=current_date) &
                                                           Q(need_video=True) &
                                                           Q(id__in=subject_knowledge_question_list_id))
            # 1自己申请过且未处理的
            elif int(applied_type) == 1:
                server_obj_list = Questions.objects.filter(Q(permit_user__id=request.user.id) &
                                                           Q(expiration__gte=current_date) &
                                                           Q(need_video=True) &
                                                           Q(id__in=subject_knowledge_question_list_id) &
                                                           Q(permitvideoofquestion__status=0))
            # 2自己未申请过的
            elif int(applied_type) == 2:
                server_obj_list = Questions.objects.filter(~Q(id__in=has_permit_question_list_id) &
                                                           Q(expiration__gte=current_date) &
                                                           Q(need_video=True) &
                                                           Q(id__in=subject_knowledge_question_list_id))
            # 3自己申请过且被批准的, 且未上传视频的
            elif int(applied_type) == 3:
                question_has_video = Video.objects.all().values('question')
                question_has_video_id = convert_dic_list(dict_list=question_has_video, key='question')
                server_obj_list = Questions.objects.filter(Q(permit_user__id=request.user.id) &
                                                           Q(expiration__gte=current_date) &
                                                           Q(need_video=True) &
                                                           Q(id__in=subject_knowledge_question_list_id) &
                                                           Q(permitvideoofquestion__status=1) &
                                                           ~Q(id__in=question_has_video_id))
            else:
                server_obj_list = Questions.objects.filter(~Q(id__in=has_permit_question_list_id) &
                                                           Q(expiration__gte=current_date) &
                                                           Q(need_video=True) &
                                                           Q(id__in=subject_knowledge_question_list_id))
            if len(server_obj_list) == 0:
                return Response(error_result(code=1099, results=[], total=0))
            # server_obj_list_values = server_obj_list.values('content_front', 'id', 'expiration', 'reference', 'order')
            server_obj_list_values = self.get_serializer(server_obj_list, many=True).data
            status_dic = {1: '申请过', 2: '未申请', 3: '已批准'}
            for i in range(len(server_obj_list_values)):
                server_obj_list_values[i]['status'] = status_dic[int(applied_type)]
            return Response(error_result(code=200, results=server_obj_list_values, total=len(server_obj_list_values)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 把pictures字段中的图片链接写入到content、answer、explain中
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def inject_pictures_into_content(self, request):
        try:
            question_obj_list = self.queryset_new.objects.all()
            for server_obj in question_obj_list:
                pictures = server_obj.pictures
                pictures_answer = server_obj.pictures_answer
                pictures_explain = server_obj.pictures_explain
                content = server_obj.content
                answer = server_obj.answer
                explain = server_obj.explain
                obj_new = {}
                modify_flag = False
                if len(pictures) > 0 and '<img' not in content:
                    # content = server_obj.content
                    obj_new = add_marked_for_pictures(content, pictures, 'content')
                    modify_flag = True
                if len(pictures_answer) > 0 and '<img' not in answer:
                    # answer = server_obj.answer
                    obj_new = add_marked_for_pictures(answer, pictures_answer, 'answer')
                    modify_flag = True
                if len(pictures_explain) > 0 and '<img' not in explain:
                    # explain = server_obj.explain
                    obj_new = add_marked_for_pictures(explain, pictures_explain, 'explain')
                    modify_flag = True
                if modify_flag:
                    serializer = self.get_serializer(instance=server_obj, data=obj_new, partial=True)
                    if not serializer.is_valid(raise_exception=False):
                        print("序列化失败：", serializer.errors)
                        return Response(error_result(code=1099, results='图片修改失败', msg=serializer.errors))
                    serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 补充图片尾部缺乏>符号
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def add_greater_than_sign_into_content(self, request):
        try:
            question_obj_list = self.queryset_new.objects.all()
            for server_obj in question_obj_list:
                content = server_obj.content
                answer = server_obj.answer
                explain = server_obj.explain
                obj_new = {}
                modify_flag = False
                if '服务器繁忙"<img' in content or '服务器繁忙' in content[-6:]:
                    obj_new = add_greater_than(content, 'content')
                    modify_flag = True
                if '服务器繁忙"<img' in answer or '服务器繁忙' in answer[-6:]:
                    obj_new = add_greater_than(answer, 'answer')
                    modify_flag = True
                if '服务器繁忙"<img' in explain or '服务器繁忙' in explain[-6:]:
                    obj_new = add_greater_than(explain, 'explain')
                    modify_flag = True
                if modify_flag:
                    serializer = self.get_serializer(instance=server_obj, data=obj_new, partial=True)
                    if not serializer.is_valid(raise_exception=False):
                        print("序列化失败：", serializer.errors)
                        return Response(error_result(code=1099, results='图片修改失败', msg=serializer.errors))
                    serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 把content、answer、explain中的非html元素转换成p、img、table标记
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def convert_tags_2_html_marked(self, request):
        try:
            question_obj_list = self.queryset_new.objects.all()
            for server_obj in question_obj_list:
                q_id = server_obj.id
                print('id:', q_id)
                content = server_obj.content
                answer = server_obj.answer
                explain = server_obj.explain
                modify_flag = False
                obj_new = {}
                if len(content) > 0 and '<p>' not in content:
                    temp_content = replace_tags_with_paragraphs(content)
                    if len(temp_content) > 0:
                        obj_new['content'] = temp_content
                        modify_flag = True
                if len(answer) == 0:
                    obj_new['answer'] = {'answer': '<p>略</p>'}
                    modify_flag = True
                elif len(answer) == 1:
                    obj_new['answer'] = {'answer': '<p>' + answer + '</p>'}
                    modify_flag = True
                elif len(answer) > 1 and '<p>' not in answer:
                    temp_content = replace_tags_with_paragraphs(answer)
                    if len(temp_content) > 0:
                        obj_new['answer'] = temp_content
                        modify_flag = True
                if len(explain) == 0:
                    obj_new['explain'] = {'explain': '<p>略</p>'}
                    modify_flag = True
                elif len(explain) == 1:
                    obj_new['explain'] = {'explain': '<p>' + explain + '</p>'}
                    modify_flag = True
                elif len(explain) > 1 and '<p>' not in explain:
                    temp_content = replace_tags_with_paragraphs(explain)
                    if len(temp_content) > 0:
                        obj_new['explain'] = temp_content
                        modify_flag = True
                if modify_flag:
                    serializer = self.get_serializer(instance=server_obj, data=obj_new, partial=True)
                    if not serializer.is_valid(raise_exception=False):
                        print("序列化失败：", serializer.errors)
                        return Response(error_result(code=1099, results='标签修改失败', msg=serializer.errors))
                    serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 把content如果是选择题，则选项A.前添加p标签
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def add_p_before_option(self, request):
        try:
            question_obj_list = self.queryset_new.objects.all()
            for server_obj in question_obj_list:
                content = server_obj.content
                question_type = server_obj.type
                modify_flag = False
                obj_new = {'content': ''}
                if len(content) > 0 and question_type == 1:
                    obj_new['content'] = add_paragraphs_before_A(content)
                    modify_flag = True
                if modify_flag:
                    serializer = self.get_serializer(instance=server_obj, data=obj_new, partial=True)
                    if not serializer.is_valid(raise_exception=False):
                        print("序列化失败：", serializer.errors)
                        return Response(error_result(code=1099, results='选项A.前添加</p><p>失败', msg=serializer.errors))
                    serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 删除多余的\n
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def delete_redundant_enter_n(self, request):
        try:
            question_obj_list = self.queryset_new.objects.all()
            for server_obj in question_obj_list:
                content = server_obj.content
                modify_flag = False
                obj_new = {'content': ''}
                if len(content) > 0:
                    obj_new['content'] = remove_newline_before_p_tag(content)
                    modify_flag = True
                if modify_flag:
                    serializer = self.get_serializer(instance=server_obj, data=obj_new, partial=True)
                    if not serializer.is_valid(raise_exception=False):
                        print("序列化失败：", serializer.errors)
                        return Response(error_result(code=1099, results='删除\\n失败', msg=serializer.errors))
                    serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 将一对多的knowledgepoint_question关系转换成多对多，即读取一条记录，将创建多对多关系
    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def convert_knowledge_manytomany(self, request):
        try:
            question_obj_list = self.queryset_new.objects.all()
            for server_obj in question_obj_list:
                knowledgepoint = server_obj.knowledgepoint
                print('knowledge:', knowledgepoint.id)
                print('question:', server_obj.id)
                new_data_dic = {'knowledgepoint': knowledgepoint.id, 'question': server_obj.id, 'owner': 3}
                serializer = KnowledgePoint2QuestionSerializer(data=new_data_dic)
                if not serializer.is_valid(raise_exception=False):
                    print("创建知识点-试题多对多失败:", serializer.errors)
                    return Response(error_result(code=1099))
                serializer.save()
            total = len(question_obj_list)
            return Response(error_result(code=200, results="多对多创建成功", total=total))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 用户上传图片或pdf文件，上传到阿里接口，最后返回试题数组
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def question_cut_alibaba(self, request, *args, **kwargs):
        try:
            # print('start accept file')
            received_file = request.FILES.get("upload_file")
            file_prefix = received_file.name.split(".")[-1]
            if file_prefix.upper() not in ['PNG', 'JPG', 'JPEG', 'BMP', 'GIF', 'TIFF', 'WEBP']:
                return Response(error_result(code=1099, msg='文件类型错误'))
            delete_all_files_in_folder(os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILES_IMAGES)
            new_name = get_new_name() + '.' + file_prefix
            file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILES_IMAGES + new_name
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            # new_file = settings.VIDEO_URL + new_name
            question_list, paper_dict = RecognizeEduPaperCutAlibaba.Sample.main(file_path)
            # print(question_list)
            return_data = {'question_list': question_list, 'paper_info': paper_dict}
            if len(question_list) > 0:
                return Response(error_result(code=200, results=return_data, msg='文件提交成功！'))
            else:
                return Response(error_result(code=1099, results='no', msg='试题为空！'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 用户上传图片或pdf文件，上传到百度接口，最后返回试题数组
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def question_cut_baidu(self, request, *args, **kwargs):
        try:
            # print('start accept file')
            received_file = request.FILES.get("upload_file")
            file_prefix = received_file.name.split(".")[-1]
            if file_prefix.upper() in ['PNG', 'JPG', 'JPEG', 'BMP', 'GIF', 'TIFF', 'WEBP']:
                file_type = 'image'
            elif file_prefix.upper() in ['PDF']:
                file_type = 'pdf'
            else:
                return Response(error_result(code=1099, msg='文件类型错误'))
            new_name = get_new_name() + '.' + file_prefix
            file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILES_IMAGES + new_name
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            # new_file = settings.VIDEO_URL + new_name
            question_list, paper_dict = RecognizeEduPaperCutBaidu.main(file_path, file_type)
            # print(question_list)
            return_data = {'question_list': question_list, 'paper_info': paper_dict}
            if len(question_list) > 0:
                return Response(error_result(code=200, results=return_data, msg='文件提交成功！'))
            else:
                return Response(error_result(code=1099, results='no', msg='试题为空！'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 用户上传base64的图片，上传到百度公式识别接口，最后返回latex公式的文本
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def formula_ocr_alibaba(self, request, *args, **kwargs):
        try:
            received_file = request.data["base64_str"]
            folder = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_TEMP_URL
            filename = 't.png'
            written_result = write_base64_image_to_file(base64_str=received_file, folder=folder, filename=filename)
            formula_str = ''
            if written_result:
                formula_str = FormulaOcrAlibaba.Sample.main(folder + filename)
            if len(formula_str) > 0:
                return Response(error_result(code=200, results=formula_str, msg='文件提交成功！'))
            else:
                return Response(error_result(code=1099, results='no', msg='试题为空！'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 用户上传base64的图片，上传到百度公式识别接口，最后返回latex公式的文本
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def formula_ocr_baidu(self, request, *args, **kwargs):
        try:
            received_file = request.data["base64_str"]
            formula_str = FormulaOcrBaidu.main(received_file)
            if len(formula_str) > 0:
                return Response(error_result(code=200, results=formula_str, msg='文件提交成功！'))
            else:
                return Response(error_result(code=1099, results='no', msg='试题为空！'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 用户上传latex公式，写入word，最后返回latex公式的word文件
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def latex_2_word(self, request, *args, **kwargs):
        try:
            latex_str = request.data["latex_str"]
            new_name = get_new_name()
            file_path = os.path.join(settings.BASE_DIR).replace('\\',
                                                                '/') + settings.IMAGE_TEMP_URL + new_name + '.docx'
            if latex_2_word.main(latex_str=latex_str, file_path=file_path):
                with open(file_path, 'rb') as f:
                    response = HttpResponse(f.read(), content_type='application/msword')
                    response['Content-Disposition'] = f'attachment; filename={new_name}.docx'
                    return response
            else:
                return Response(error_result(code=1099, results='no', msg='公式错误！'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 前端上传tex或者zip文件，发给flask端，渲染成pdf文件，并将该pdf文件返回给前端
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def tex_2_pdf(self, request, *args, **kwargs):
        try:
            print('start accept file')
            received_file = request.FILES.get("upload_file", None)
            file_prefix = received_file.name.split(".")[-1]
            if file_prefix.upper() not in ['TEX', 'ZIP']:
                return Response(error_result(code=1099, msg='文件类型错误'))
            new_name = 'b' + '.' + file_prefix
            file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILES_IMAGES + new_name
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            with open(file_path, 'rb') as tex_file:
                tex_file_content = tex_file.read()
                url = "http://117.72.37.184:5000/tex2pdf"
                headers = {'Content-Type': 'application/octet-stream', 'Return-File-Type': 'pdf',
                           'File-Type': file_prefix}
                response = requests.post(url, data=tex_file_content, headers=headers)
                print('收到flask端发来的结果：', response)
                return_file_path = "/static/temp/tex/b.pdf"
                pdf_file = os.path.join(settings.BASE_DIR).replace('\\', '/') + return_file_path
                if response.status_code == 200:
                    # 以文件链接形式发送给前端
                    with open(pdf_file, 'wb') as pdf_file:
                        pdf_file.write(response.content)
                    return Response(error_result(code=200, results=return_file_path))
                return Response(error_result(code=500, results="错误"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 前端上传pdf文件，返回zip解析后的latex文件
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def pdf_2_latex(self, request, *args, **kwargs):
        try:
            print('start accept pdf file')
            # 默认文件的属性名为upload_file: md格式；另外还有upload_file_tex和upload_file_docx
            received_file = request.FILES.get("upload_file", None)
            to_file_type = 'md'
            if not received_file:
                received_file = request.FILES.get("o", None)
                to_file_type = 'tex'
                if not received_file:
                    received_file = request.FILES.get("upload_file_docx", None)
                    to_file_type = 'docx'
            file_prefix = received_file.name.split(".")[-1]
            if file_prefix.upper() not in ['PDF']:
                return Response(error_result(code=1099, msg='文件类型错误'))
            new_name = 'b' + '.pdf'
            file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILES_IMAGES + new_name
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            with open(file_path, 'rb'):
                if to_file_type == 'upload_file' or to_file_type == 'upload_file_tex':
                    new_name = get_new_name() + '.zip'
                else:
                    new_name = get_new_name() + '.docx'
                return_file_path = f"{settings.IMAGE_TEMP_URL}{new_name}"
                zip_file = os.path.join(settings.BASE_DIR).replace('\\', '/') + return_file_path
                status = Doc2X.main(file_path, zip_file, to_file_type)
                if status == 'success':
                    # 以文件链接形式发送给前端
                    file_url = settings.IMAGE_TEMP_URL + new_name
                    return Response(error_result(code=200, results=file_url))
            return Response(error_result(code=500, results="错误"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 前端上传pdf文件，返回zip，利用minerU的官方api实现
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def pdf_2_other_mineru(self, request, *args, **kwargs):
        try:
            print('start accept pdf file')
            # 默认文件的属性名为upload_file: md格式；另外还有upload_file_tex和upload_file_docx
            received_file = request.FILES.get("upload_file", None)
            file_prefix = received_file.name.split(".")[-1]
            if file_prefix.upper() not in ['PDF']:
                return Response(error_result(code=1099, msg='文件类型错误'))
            new_name = str(time.time()) + '.pdf'
            file_url = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILE_TEMP_URL + new_name
            file_path = settings.SERVER_IP + settings.FILE_TEMP_URL + new_name
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            with open(file_path, 'rb'):
                status, return_url = mineru(file_name=file_url)
                if status:
                    return Response(error_result(code=200, results=return_url))
            return Response(error_result(code=500, results="错误"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 前端上传pdf文件的url，返回zip，利用minerU的官方api实现
    @action(methods=['post'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def pdf_2_other_mineru_url(self, request, *args, **kwargs):
        try:
            file_url = request.data["file_url"]
            status, return_url = mineru(file_url=file_url)
            if status:
                return Response(error_result(code=200, results=return_url))
            return Response(error_result(code=500, results="错误"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class QuestionPublicAndOnlineApiView(APIView):

    def get(self, request):
        queryset = Questions.IsPublicAndOnline.all()
        count = len(queryset)
        # 1. 实例化分页器对象
        page_obj = MyPage()
        # 2. 使用自己配置的分页器调用分页方法进行分页
        page_data = page_obj.paginate_queryset(queryset, request)
        # 3. 序列化我们分页好的数据
        ser_obj = QuestionSerializer(page_data, many=True)
        # 4. 返回数据
        my_response_data = {'count': count, 'results': ser_obj.data}
        return Response(error_result(code=200, msg=data_query_success_text, results=my_response_data))


class KnowledgePointContainQuestionViewSet(CustomModelViewSet):
    queryset = KnowledgePoint.objects.all()
    serializer_class = KnowledgePointContainQuestionSerializer
    pagination_class = SmallPage

    def get_queryset(self):
        queryset = super(KnowledgePointContainQuestionViewSet, self).get_queryset()
        section = self.request.query_params.get('section')
        if section:
            queryset = queryset.filter(section=section)
        return queryset


class SectionContainQuestionViewSet(CustomModelViewSet):
    queryset = Section.objects.all()
    serializer_class = SectionContainQuestionSerializer
    pagination_class = SmallPage

    def get_queryset(self):
        queryset = super(SectionContainQuestionViewSet, self).get_queryset()
        chapter = self.request.query_params.get('chapter')
        if chapter:
            queryset = queryset.filter(chapter=chapter)
        return queryset


class ChapterContainQuestionViewSet(CustomModelViewSet):
    queryset = Chapter.objects.all()
    serializer_class = ChapterContainQuestionSerializer
    pagination_class = SmallPage

    def get_queryset(self):
        queryset = super(ChapterContainQuestionViewSet, self).get_queryset()
        course = self.request.query_params.get('course')
        if course:
            queryset = queryset.filter(course=course)
        return queryset


class ImageOfQuestionViewSet(CustomNoQuestionsModelViewSet):
    queryset = ImageOfQuestion.objects.all()
    serializer_class = ImageOfQuestionModelSerializer


class GeneratePaper2Database(APIView):
    """
    post 函数把题目列表和试卷都持久化到数据表中，并且关联试卷和题目，多对多
    分为2种情况：新建试卷并新增题目，和补充题目并关联到试卷中
    """

    def post(self, request):

        try:
            global paper_obj
            owner = self.request.user
            import_type = request.data['type']
            # 新增题目，更新试卷，即数据表中已有试卷的信息，不需要再次新建
            if import_type == 'importQuestionsSupplementPaper':
                paper_id = int(request.data['id'])
                paper_obj = ExaminationPaper.objects.filter(id=paper_id).first()
                if not paper_obj:
                    return Response(error_result(msg="找不到指定的试卷。建议返回试卷页面，点击正确的链接"))
            # 新增试卷和题目，即数据表中没有试卷信息，此时需要新建该试卷
            elif import_type == 'importQuestionsAndPaper' or import_type == 'ai_cut':
                paper_information_dict = request.data["paperInformation"]
                # print('paper:', paper_information_dict)
                paper_information_dict["owner"] = owner
                paper_information_dict["online"] = False
                # 前端可能存在数值的bug
                if 'is_joint' in paper_information_dict:
                    if paper_information_dict["is_joint"] == 'true' or paper_information_dict["is_joint"]:
                        paper_information_dict["is_joint"] = True
                    else:
                        paper_information_dict["is_joint"] = False
                else:
                    paper_information_dict["is_joint"] = True
                subject_id = 1
                subject_id_request = paper_information_dict.get('subject')
                if subject_id_request:
                    subject_id = subject_id_request
                subject_obj = Subject.objects.get(pk=subject_id)
                paper_information_dict["subject"] = subject_obj
                paper_obj = ExaminationPaper.objects.create(**paper_information_dict)
                if not paper_obj:
                    return Response(error_result(code=1099, msg="操作失败,试卷错误！"))
            else:
                return Response(error_result(code=1099, msg="import_type参数错误！"))
            question_list = request.data["questionList"]
            # print('question_list:', question_list)

            for question in question_list:
                question["owner"] = owner
                destination_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_URL
                if '/temp/' in question['content'] and 'src' in question['content']:
                    image_list = extract_image_paths(question['content'])
                    for image in image_list:
                        copy_and_delete(image, destination_path)
                    question['content'] = question['content'].replace('temp/', 'images/')
                if '/temp/' in question['answer'] and 'src' in question['answer']:
                    image_list = extract_image_paths(question['answer'])
                    for image in image_list:
                        copy_and_delete(image, destination_path)
                    question['answer'] = question['answer'].replace('temp/', 'images/')
                if '/temp/' in question['explain'] and 'src' in question['explain']:
                    image_list = extract_image_paths(question['explain'])
                    for image in image_list:
                        copy_and_delete(image, destination_path)
                    question['explain'] = question['explain'].replace('temp/', 'images/')
                knowledgepoint_id_list = question["knowledgepoint"]
                del question["knowledgepoint"]
                question_obj = Questions.objects.create(**question)
                knowledgepoint_server_list = KnowledgePoint.objects.filter(id__in=knowledgepoint_id_list)
                question_obj.knowledgepoint_set.add(*knowledgepoint_server_list)
                question_obj.save()
                paper_obj.questions.add(question_obj)
            paper_obj.online = False
            paper_obj.save()
            is_joint = paper_obj.is_joint

            # 创建试卷和题目成功后，同时创建该试卷的申请上线表单
            if import_type == 'importQuestionsAndPaper':
                ten_seconds = datetime.now() - timedelta(seconds=10)
                paper_server = ExaminationPaper.objects.filter(
                    Q(owner__id=request.user.id) & Q(add_time__gte=ten_seconds))
                if not paper_server:
                    return Response(error_result(code=201, msg=data_upload_ok_online_fail_text, results='yes'))
                paper_server = paper_server.last()
                application_dic = {'paper': paper_server.pk, 'user': request.user.id, 'status': 0}
                if is_joint:
                    application_serializer = JointPaperApplicationModelSerializer(data=application_dic)
                else:
                    application_serializer = GeneralPaperApplicationModelSerializer(data=application_dic)
                if not application_serializer.is_valid():
                    return Response(error_result(code=201, msg=data_upload_ok_online_fail_text, results='yes'))
                application_serializer.save()
            # 添加试卷和题目成功后，同时修改该试卷的申请上线表单状态为3（表示已修改）
            elif import_type == 'importQuestionsSupplementPaper':
                paper_id = int(request.data['id'])
                if is_joint:
                    application_server = JointPaperApplication.objects.filter(paper__id=paper_id)
                else:
                    application_server = GeneralPaperApplication.objects.filter(paper__id=paper_id)
                if not application_server:
                    return Response(error_result(code=201, msg=data_upload_ok_online_fail_text, results='yes'))
                application_server = application_server.first()
                application_dic = {'status': 3, 'user': owner.id, 'paper': paper_id}
                application_serializer = JointPaperApplicationModelSerializer(instance=application_server,
                                                                              data=application_dic)
                if not application_serializer.is_valid():
                    print(application_serializer.errors)
                    return Response(error_result(code=201, msg=data_upload_ok_online_fail_text, results='yes'))
                application_serializer.save()
            return Response(error_result(code=200, msg="上传成功，申请上线成功！", results='yes'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class PersistenceManyQuestions2DatabaseAPIView(APIView):
    """
    post 函数把多个题目列表持久化到数据表中
    """

    def post(self, request):
        try:
            owner = self.request.user
            question_list = request.data["questionList"]
            for question in question_list:
                question["owner"] = owner
                knowledgepoint_id_list = question["knowledgepoint"]
                del question["knowledgepoint"]
                question_obj = Questions.objects.create(**question)
                knowledgepoint_server_list = KnowledgePoint.objects.filter(id__in=knowledgepoint_id_list)
                question_obj.knowledgepoint_set.add(*knowledgepoint_server_list)
                question_obj.save()
            length = len(question_list)
            data = "恭喜录入成功，共录入" + str(length) + "个题目"
            return Response(error_result(code=200, msg=data, results=data, total=length))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class ExaminationPaperViewSet(CustomNoQuestionsModelViewSet):
    queryset = ExaminationPaper.objects.all()
    queryset_new = ExaminationPaper
    serializer_classes = {
        'list': ExaminationPaperModelSerializer,
        'retrieve': ExaminationPaperModelSerializer,
        'create': ExaminationPaperModelDeSerializer,
        'update': ExaminationPaperModelDeSerializer,
    }
    default_serializer_class = ExaminationPaperModelDeSerializer
    filter_fields = ('title', 'owner', 'publish_time')
    pagination_class = SmallPage

    def get_serializer_class(self):
        return self.serializer_classes.get(self.action, self.default_serializer_class)

    def get_queryset(self):
        queryset = super(ExaminationPaperViewSet, self).get_queryset()
        return queryset

    # 申请解析指定的试卷
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def permit_explain_create(self, request, *args, **kwargs):
        try:
            paper_id = request.data['paper_id']
            obj_server = PermitExplainPaperOfUser.objects.filter(Q(user__id=request.user.id) &
                                                                 Q(paper__id=paper_id))
            if obj_server:
                return Response(error_result(code=1099, msg='你已经申请过，不能重复申请', results='no'))
            new_data_dic = {'user': request.user.id, 'paper': paper_id, 'status': False}
            serializer = PermitExplainPaperOfUserModelSerializer(data=new_data_dic)
            if not serializer.is_valid(raise_exception=False):
                print("申请解析的序列化错误:", serializer.errors)
                return Response(error_result(code=1099))
            serializer.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 管理员审批用户的申请解析试卷, 结果发给申请者通知
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            permission_classes=[IsAdmin]
            )
    def permit_explain_approval(self, request, *args, **kwargs):
        try:
            paper_id = request.data['paper_id']
            user_id = request.data['user_id']
            status = request.data['status']
            obj_server = PermitExplainPaperOfUser.objects.filter(Q(user__id=user_id) & Q(paper__id=paper_id))
            if not obj_server:
                return Response(error_result(code=1099, msg='申请的用户id或试卷id有误', results='no'))
            modify_dic = {'paper': paper_id, 'user': user_id, 'status': status}
            obj_server = obj_server.first()
            serializer = PermitExplainPaperOfUserModelSerializer(instance=obj_server, data=modify_dic, partial=True)
            if not serializer.is_valid():
                return Response(error_result(code=406, msg=data_error_text, results=serializer.errors))
            serializer.save()
            state_cn = {False: '禁止', True: '批准'}
            create_send_notification.delay(user=user_id, content='你申请解析的试卷”' + obj_server.paper.title + '”，状态为'
                                                                 + state_cn[status])
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 每次一份试卷添加一个题目，要求提交者是该试卷的允许名单,前端提供试卷id，题目的字典(含题目的必备字段)
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication]
            )
    def add_one_question(self, request, *args, **kwargs):
        try:
            paper_id = request.data['paper_id']
            permit_obj_server = PermitExplainPaperOfUser.objects.filter(Q(user__id=request.user.id) &
                                                                        Q(paper__id=paper_id))
            if not permit_obj_server:
                return Response(error_result(code=1099, msg='你还没被允许解析该试卷，请联系管理员', results='no'))
            paper_obj_server = ExaminationPaper.objects.get(id=paper_id)
            question_dic = request.data['question_dic']
            knowledge_id = question_dic['knowledgepoint']
            knowledge_obj_server = KnowledgePoint.objects.get(id=knowledge_id)
            question_dic['knowledgepoint'] = knowledge_obj_server
            question_dic['owner_id'] = request.user.id
            question_obj = Questions.objects.create(**question_dic)
            paper_obj_server.questions.add(question_obj)
            paper_obj_server.save()
            return Response(error_result(code=200, results="ok"))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 参数applied_type取值范围为[0, 1, 2]，0代表全部，1代表自己申请过的；2代表未申请的。且所有试卷是未过期的。
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication]
            )
    def get_revisable_paper(self, request):
        try:
            server_obj_list = []
            applied_type = request.query_params.get('applied_type')
            subject_id = request.query_params.get('subject_id')
            current_date = datetime.now()
            has_permit_paper_list = PermitExplainPaperOfUser.objects.filter(user__id=request.user.pk).values("paper")
            if not subject_id:
                subject_id = 1
            elif int(subject_id) < 1 or int(subject_id) > 3:
                subject_id = 1
            else:
                subject_id = int(subject_id)
            if not applied_type:
                server_obj_list = ExaminationPaper.objects.filter(Q(expiration__gte=current_date) &
                                                                  Q(revisable=True) &
                                                                  Q(subject__id=subject_id))
            elif int(applied_type) == 0:
                server_obj_list = ExaminationPaper.objects.filter(Q(expiration__gte=current_date) &
                                                                  Q(revisable=True) &
                                                                  Q(subject__id=subject_id))
            elif int(applied_type) == 1:
                server_obj_list = ExaminationPaper.objects.filter(Q(id__in=has_permit_paper_list) &
                                                                  Q(expiration__gte=current_date) &
                                                                  Q(revisable=True) &
                                                                  Q(subject__id=subject_id))
            elif int(applied_type) == 2:
                server_obj_list = ExaminationPaper.objects.filter(~Q(id__in=has_permit_paper_list) &
                                                                  Q(expiration__gte=current_date) &
                                                                  Q(revisable=True) &
                                                                  Q(subject__id=subject_id))
            else:
                server_obj_list = ExaminationPaper.objects.filter(Q(expiration__gte=current_date) &
                                                                  Q(revisable=True) &
                                                                  Q(subject__id=subject_id))
            if len(server_obj_list) == 0:
                return Response(error_result(code=1099, results='no', total=0))
            page = self.paginate_queryset(server_obj_list)
            serializer = ExaminationPaperModelSerializer(page, many=True)
            if not serializer:
                return Response(error_result(code=1099, results='no', total=0))
            return Response(error_result(code=200, results=serializer.data, total=len(server_obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 当前用户申请过的，且未过期的所有试卷
    @action(methods=['get'], detail=False)
    def has_applied_paper(self, request):
        try:
            current_date = datetime.now()
            has_applied_paper_list = PermitExplainPaperOfUser.objects.filter(Q(user__id=request.user.pk) &
                                                                             Q(paper__expiration__gte=current_date)
                                                                             ).values("paper")
            if not has_applied_paper_list:
                return Response(error_result(code=1099, results='no', total=0))
            return Response(error_result(code=200, results=has_applied_paper_list, total=len(has_applied_paper_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 当前用户申请过且已经被批准的，且未过期的所有试卷
    @action(methods=['get'], detail=False)
    def has_applied_and_approved_paper(self, request):
        try:
            current_date = datetime.now()
            has_applied_paper_list = PermitExplainPaperOfUser.objects.filter(Q(user__id=request.user.pk) &
                                                                             Q(paper__expiration__gte=current_date) &
                                                                             Q(status=1)
                                                                             ).values("paper")
            if not has_applied_paper_list:
                return Response(error_result(code=1099, results='no', total=0))
            return Response(error_result(code=200, results=has_applied_paper_list, total=len(has_applied_paper_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 某个学科下的练习知识点-小程序， 定制字典key的value, text等字段，为了①直接适配前端的卡片显示,②减少网络开销
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_paper_by_subject_and_year_month_for_exercise(self, request):
        try:
            subject_id = request.query_params.get('subject_id')
            if subject_id is None:
                subject_id = 1
            year = request.query_params.get('year')
            month = request.query_params.get('month')
            if year is None or month is None:
                paper_list_server = ExaminationPaper.objects.filter(subject__id=subject_id)
            else:
                paper_list_server = ExaminationPaper.objects.filter(Q(subject__id=subject_id) &
                                                                    Q(title__contains=year) &
                                                                    Q(title__contains=month) &
                                                                    Q(online=True) &
                                                                    Q(open_level='public'))
            if not paper_list_server:
                return Response(error_result(code=1099, results=[], msg='找不到当前筛选条件下的试卷'))
            # 获取所有已做题目的id列表
            all_has_done_questions = UserDoQuestionLog.objects.filter(user__id=request.user.id)
            all_has_done_questions_ids = []
            if all_has_done_questions is not None:
                all_has_done_questions = all_has_done_questions.values('question')
                all_has_done_questions_ids = convert_dic_list(dict_list=all_has_done_questions, key='question')
            paper_list_dic = []
            for paper_item in paper_list_server:
                # this_paper_questions = paper_item.questions
                this_paper_questions = QuestionOfPaper.objects.filter(Q(paper__id=paper_item.id) &
                                                                      Q(question__online=True) &
                                                                      Q(question__open_level='public'))
                if this_paper_questions and len(this_paper_questions) > 0:
                    this_paper_questions_ids = convert_dic_list(dict_list=this_paper_questions.values('id'))
                    intersection_list = intersection(all_has_done_questions_ids, this_paper_questions_ids)
                    # print('all_has_done_questions_ids', all_has_done_questions_ids)
                    # print('this_knowledge_questions_ids', this_paper_questions_ids)
                    has_done_rate = int(len(intersection_list) / len(this_paper_questions) * 100)
                    has_done_rate_str = str(has_done_rate) + '%'
                    paper_dic = {
                        'has_done': len(intersection_list),
                        'questions_amount': len(this_paper_questions),
                        'rate': has_done_rate_str,
                        'name': paper_item.title,
                        'id': paper_item.id,
                    }
                else:
                    paper_dic = {
                        'has_done': 0,
                        'questions_amount': 0,
                        'rate': '0%',
                        'name': paper_item.title,
                        'id': paper_item.id,
                    }
                paper_list_dic.append(paper_dic)
            return Response(error_result(code=200, results=paper_list_dic, total=len(paper_list_dic)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 获取最新的年月列表二维嵌套字典数组，
    # 如[{'name':'2024年', 'children':[{'name:'12月', 'year_month': ['2024年', '12月']}……]},
    # {'name':'2023年', 'children':[{'name:'12月', 'year_month': ['2023年', '12月']}……
    # ]，其中month_id用于前端显示那个月份别选中
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_year_month(self, request):
        try:
            now = datetime.now()
            date_head = now.strftime("%Y-%m-%d")
            year_head = int(date_head[:4])
            month_head = int(date_head[5:7])

            date_tail = '2017-01-01'
            year_tail = int(date_tail[:4])
            menu_list = []
            year_name = str(year_head) + '年'
            year_item = {'name': year_name, 'children': []}
            month_id = 1
            # 第一年的月份特殊处理
            for i in range(month_head, 0, -1):
                month_name = str(i) + '月'
                month_item = {'name': month_name, 'year_month': [year_name, month_name], 'id': month_id}
                year_item['children'].append(month_item)
                month_id += 1
            menu_list.append(year_item)
            # 除第一年的剩下年份，按照规律处理
            for j in range(year_head - 1, year_tail - 1, -1):
                year_name = str(j) + '年'
                year_item = {'name': year_name, 'children': []}
                for i in range(12, 0, -1):
                    month_name = str(i) + '月'
                    month_item = {'name': month_name, 'year_month': [year_name, month_name], 'id': month_id}
                    year_item['children'].append(month_item)
                    month_id += 1
                menu_list.append(year_item)
            return Response(error_result(code=200, msg='OK', results=menu_list, total=len(menu_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, results={}))

    # 筛选某份试卷下的题目，且筛选是否已做题目,has_done为True表示题目列表可以包含已做题要，False表示不要已做题
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_question_filter_paper_and_has_done(self, request):
        """筛选指定试卷id下，指定用户id为owner下的题目"""
        try:
            current_user_server = User.objects.get(id=request.user.id)
            paper_id = request.query_params.get('paper')
            if paper_id is None:
                return Response(error_result(code=1099, results=[]))
            paper_id = int(paper_id)
            has_done = request.query_params.get('has_done')
            if has_done and int(has_done) == 1:
                has_done = True
            elif has_done and int(has_done) == 2:
                has_done = False
            elif has_done is None:
                has_done = True
            if has_done:
                question_of_paper_list_server = QuestionOfPaper.objects.filter(Q(question__online=True) &
                                                                               Q(question__open_level='public') &
                                                                               Q(paper__id=paper_id))
            else:
                has_done_list_id = convert_dic_list(current_user_server.do_question.values('id'))
                question_of_paper_list_server = QuestionOfPaper.objects.filter(Q(question__online=True) &
                                                                               Q(question__open_level='public') &
                                                                               Q(paper__id=paper_id) &
                                                                               ~Q(question__id__in=has_done_list_id))
            # print('question_of_paper_list:', question_of_paper_list_server)
            if not question_of_paper_list_server:
                return Response(error_result(code=1099, results=[], msg='该关键字无内容'))
            question_of_paper_list_id = convert_dic_list(question_of_paper_list_server.values('question'),
                                                         key='question')
            question_list_server_new = Questions.objects.filter(id__in=question_of_paper_list_id)
            serializer = QuestionSerializer(question_list_server_new, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(question_list_server_new)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, results={}))

    # 支付下载文件pdf的费用
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def pay_for_download_file(self, request):
        try:
            paper_id = request.query_params.get('paper_id')
            paper_obj_server = ExaminationPaper.objects.get(id=paper_id)
            bank_point = paper_obj_server.bank_point
            download_log = DownloadExaminationPaperLog.objects.filter(Q(user__id=request.user.pk) &
                                                                      Q(examination_paper__id=paper_id))
            username_company = Company.objects.first().president
            user_company = User.objects.get(username=username_company)
            # 如果下载文件是自己的，不扣钱
            if paper_obj_server.owner.id == request.user.id:
                print('是自己的文件，不扣费')
                return Response(error_result(code=201, results=0, total=1))
            # 如果未下载过， 且不是自己的资源，则需要扣钱
            if len(download_log) == 0:
                new_data = {'user': request.user.pk, 'examination_paper': paper_id}
                download_log_serializer = DownloadExaminationPaperLogModelSerializer(data=new_data)
                if not download_log_serializer.is_valid():
                    print('下载记录创建记录时序列化错误：', download_log_serializer.errors)
                else:
                    download_log_serializer.save()
                    create_customer_bank_point_log_change.delay(operator=request.user.pk,
                                                                receiver=user_company.pk,
                                                                point_amount=bank_point,
                                                                transaction_type=15,
                                                                remarks='下载试卷文件pdf')
                    user = User.objects.get(id=request.user.id)
                    personal_bankpoint = user.bank_point
                    print('扣费成功')
                    return Response(error_result(code=200, results=personal_bankpoint, total=1))
            # 曾经下载过了，不需要扣钱
            else:
                print('曾经下载过该文件，不扣费')
                return Response(error_result(code=201, results=0, total=1))

        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, results={}))


# 把pictures属性转换成images属性，即从字符串转化成数组
def convert_pictures_to_images(pictures):
    images = []
    if pictures == '':
        images = []
    elif "-" not in pictures:
        images.append(settings.SERVER_DEV_URL + pictures)
    else:
        images_temp = pictures.split("-")
        for image in images_temp:
            images.append(settings.SERVER_DEV_URL + image)
    return images


# 将试题中的内容、答案、解析的回车符\n转换成br、\t转换成单空格
def adapter_for_front(question_list):
    for i in range(len(question_list)):
        item = question_list[i]
        item['content_front'] = convert_enter_2_br(item['content'])
        item['answer_front'] = convert_enter_2_br(item['answer'])
        item['explain_front'] = convert_enter_2_br(item['explain'])
        item.pop("content", None)
        item.pop("answer", None)
        item.pop("explain", None)
        # 转换题干的插图
        item['images'] = convert_pictures_to_images(item['pictures'])
        item.pop("pictures")
        # 转换答案的插图
        item['images_answer'] = convert_pictures_to_images(item['pictures_answer'])
        item.pop("pictures_answer")
        # 转换解析的插图
        item['images_explain'] = convert_pictures_to_images(item['pictures_explain'])
        item.pop("pictures_explain")
    return question_list


# 将试题中的内容、答案、解析的回车符\n转换成br、\t转换成单空格(职高计算机专用，因为没有answer_picture和explain_picture
def adapter_for_front_vocational(question_list):
    for i in range(len(question_list)):
        item = question_list[i]
        item['content_front'] = convert_enter_2_br(item['content'])
        item['answer_front'] = convert_enter_2_br(item['answer'])
        item['explain_front'] = convert_enter_2_br(item['explain'])
        item.pop("content", None)
        item.pop("answer", None)
        item.pop("explain", None)
        # 转换题干的插图
        item['images'] = convert_pictures_to_images(item['pictures'])
        item.pop("pictures")
    return question_list


class Docx2QuestionViewSet(CustomNoQuestionsModelViewSet):
    """
    create: 创建单个练习，并把练习里面的题目读取，返回该练习卷例的所有题目。以列表的形式。比如[{"id":1,"content":"以下说法正确的是：","answer":"A"……}]
    """
    queryset = ExaminationPaper.objects.all()
    serializer_class = Docx2QuestionModelSerializer

    def create(self, request, *args, **kwargs):
        request.data['owner'] = request.user.id
        request.data['chapter'] = int(request.data['chapter'])
        serializer = self.get_serializer(data=request.data)
        res = serializer.is_valid(raise_exception=False)
        if res:
            self.perform_create(serializer)
            doc = ReadDoc2List.docx.Document(request.data["file"])
            flag, error_message, question_list, paper_info = get_docx_text(doc)
            adapter_for_front(question_list)
            # print(question_list)
            return Response(error_result(code=200, msg="文件上传成功！", results=question_list))
        else:
            return Response(error_result(code=500, msg="操作失败, 反序列化失败！"))


# 保存文档文件到服务器，并返回该文档的url,
def save_document(file):
    try:
        result_flag = True
        file_info, suffix = os.path.splitext(file.name)
        if suffix.upper() not in ['.DOCX', '.DOC', '.PDF']:
            return Response(error_result(code=409, msg='上传失败！'))
        # 保存文件
        new_name = get_new_name() + suffix
        # where将要保存的地址和文件名称
        where = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILE_URL + new_name

        # 分块保存文档
        content = file.chunks()
        with open(where, 'wb') as f:
            for i in content:
                f.write(i)
        file_url = settings.SERVER_IP + settings.FILE_URL + new_name
        file_path_no_https = settings.FILE_URL + new_name
        return file_url, file_path_no_https, result_flag
    except Exception as e:
        print('Exception:', e)
        return None, None, False


# 保存文档文件到服务器的static/file_temp目录下，并返回该文档的url,
def save_document_temp(file):
    try:
        result_flag = True
        file_info, suffix = os.path.splitext(file.name)
        if suffix.upper() not in ['.DOCX', '.DOC', '.PDF']:
            return Response(error_result(code=409, msg='上传失败！'))
        # 保存文件
        new_name = get_new_name() + suffix
        # where将要保存的地址和文件名称
        where = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.FILE_TEMP_URL + new_name

        # 分块保存文档
        content = file.chunks()
        with open(where, 'wb') as f:
            for i in content:
                f.write(i)
        file_url = settings.SERVER_IP + settings.FILE_TEMP_URL + new_name
        file_path_no_https = settings.FILE_TEMP_URL + new_name
        return file_url, file_path_no_https, result_flag
    except Exception as e:
        print('Exception:', e)
        return None, None, False


# 保存图片文件到服务器，并返回该图片的url,
def save_image(images):
    file_info, suffix = os.path.splitext(images.name)
    if suffix.upper() not in ['.JPG', '.JPEG', '.PNG']:
        return Response(error_result(code=409, msg='上传失败！'))
    # 保存文件
    new_name = get_new_name() + suffix
    # where将要保存的地址和文件名称
    where = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_URL + new_name

    # 分块保存image
    content = images.chunks()
    with open(where, 'wb') as f:
        for i in content:
            f.write(i)
    images_url = settings.SERVER_IP + settings.IMAGE_URL + new_name
    # images_path = where
    images_path_no_https = settings.IMAGE_URL + new_name
    return images_url, images_path_no_https


# 保存图片文件到服务器，并返回该图片的url,
def save_image_width_height(images):
    file_info, suffix = os.path.splitext(images.name)
    if suffix.upper() not in ['.JPG', '.JPEG', '.PNG']:
        return Response(error_result(code=409, msg='上传失败！'))
    # 保存文件
    new_name = get_new_name() + suffix
    # where将要保存的地址和文件名称
    where = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_URL + new_name

    # 分块保存image
    content = images.chunks()
    with open(where, 'wb') as f:
        for i in content:
            f.write(i)
    images_url = settings.SERVER_IP + settings.IMAGE_URL + new_name
    # images_path = where
    images_path_no_https = settings.IMAGE_URL + new_name
    image = Image.open(where)
    width = image.size[0]
    height = image.size[1]
    return images_url, images_path_no_https, width, height


# 保存图片文件到服务器，并返回该图片的url
def save_image_base64(images):
    file_info, suffix = os.path.splitext(images.name)
    if suffix.upper() not in ['.JPG', '.JPEG', '.PNG']:
        return Response(error_result(code=409, msg='上传失败！'))
    # 保存文件
    new_name = get_new_name() + suffix
    where = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_URL + new_name
    # 分块保存image
    content = images.chunks()
    with open(where, 'wb') as f:
        for i in content:
            f.write(i)
    with open(where, "rb") as image_file:
        encoded_string_base64 = base64.b64encode(image_file.read())
    images_path = where
    return encoded_string_base64, images_path


# 接收上传的文档文件，保存到服务器，并返回该文档的url
class UploadFileView(APIView):

    def post(self, request):
        """
        只处理单张个文件，返回url
        :param request: 单个文件
        :return: 成功返回URL
        """
        try:
            # 获取文档文件，并重命名，防止文件名重复
            file = request.FILES.get('file')
            if file is None:
                return Response(error_result(code=1099))
            file_url, file_path, result_flag = save_document(file)
            if not result_flag:
                return Response(error_result(code=1099, msg='服务器已经收到文件，但保存失败！'))
            return Response(error_result(code=200, msg="文件上传成功！返回文件路径", results=file_path))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


# 接收上传的文档文件，保存到服务器，并返回该文档的url
class UploadFileTempView(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request):
        """
        只处理单张个文件，返回url
        :param request: 单个文件
        :return: 成功返回URL
        """
        try:
            # 获取文档文件，并重命名，防止文件名重复
            file = request.FILES.get('upload_file')
            if file is None:
                return Response(error_result(code=1099))
            file_url, file_path, result_flag = save_document_temp(file)
            if not result_flag:
                return Response(error_result(code=1099, msg='服务器已经收到文件，但保存失败！'))
            return Response(error_result(code=200, msg="文件上传成功！返回文件路径", results=file_url))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class UploadImageView(APIView):
    """接收上传的图片文件，保存到服务器，并返回该图片的url"""

    def post(self, request):
        """
        只处理单张上传图片，返回url
        :param request: 单个图片文件
        :return: 成功返回URL
        """
        try:
            # 获取图片文件，并重命名，防止文件名重复
            images = request.FILES.get('images')
            need_width_height = request.data.get('need_width_height')
            if not images:
                return Response(error_result(code=500))
            no_http = request.POST.get('no_http')
            if not need_width_height:
                images_url, images_path = save_image(images)
                if no_http:
                    return Response(error_result(code=200, msg="图片上传成功！返回文件路径", results=images_path))
                else:
                    return Response(error_result(code=200, msg="图片上传成功！返回带http服务器域名", results=images_url))
            else:
                images_url, images_path, width, height = save_image_width_height(images)
                if no_http:
                    return_data = images_path
                    return Response(error_result(code=200, msg="图片上传成功！返回文件路径", results=return_data))
                else:
                    return_data = images_url
                    return Response(error_result(code=200, msg="图片上传成功！返回文件路径", results=return_data))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class FaceInspectorAPIView(APIView):

    def post(self, request):
        """
        上传一张头像图片文件，将该图片发给腾讯云人工智能平台接口，处理返回的人脸数据
        :param request: 人脸数据
        :return:
        """
        try:
            images = request.FILES['images']
            if not images:
                return Response(error_result(code=500, msg="操作失败, 反序列化失败！"))
            url, images_path = save_image(images)
            face_data = face_detection(url)
            if not face_data:
                return Response(error_result(code=500, msg="腾讯服务发送来为ong！"))
            # print("face_data:", face_data)
            os.remove(images_path)
            face_attributes_info = face_data.FaceInfos[0].FaceAttributesInfo

            return Response(error_result(code=200, msg=images_path, results=face_attributes_info.to_json_string()))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class VerifyToken(APIView):
    def get(self, request):
        try:
            user_pk = request.user.pk
            user = User.objects.get(pk=user_pk)
            serializer = UserSimpleModelSerializer(user)
            if not serializer:
                print("序列化错误：", serializer.errors)
                print(serializer.data)
            return Response(error_result(msg='验证码有效', results=serializer.data, code=200))
        except Exception as E:
            print('verifytoken error:', E)
            return Response(error_result(code=500))


class VerifyPaperName(APIView):
    def get(self, request):
        paper_title = request.query_params.get("paper_title", 0)
        paper = ExaminationPaper.objects.filter(title__contains=paper_title).first()
        if paper:
            return Response(error_result(code=1014, msg='操作失败！', results={"已经存在该试卷标题"}))
        else:
            return Response(error_result(code=200, msg=data_ok, results="恭喜，该标题可用"))


def save_file(received_file, filename):
    try:
        ff = open(filename, 'wb')
        for chunk in received_file.chunks():
            ff.write(chunk)
        ff.close()
        return 1
    except Exception as E:
        print('Exception:', E)
        return 0


# 读取文件拓展名
def get_file_suffix(file):
    # 首先找到文件名中最后一个'.'的位置
    dot_index = file.rfind('.')
    # 如果找到了'.'
    if dot_index != -1:
        # 返回'.'后面的部分作为扩展名
        return file[dot_index + 1:]
    # 如果没有找到'.'，则返回空字符串
    else:
        return "error"


# 此函数用于删除指定目录下的所有文件及文件夹
def delete_all_in_md_folder(md_folder):
    try:
        # 先删除目录下的所有文件
        for file_name in os.listdir(md_folder):
            file_path = os.path.join(md_folder, file_name)
            if os.path.isfile(file_path):
                os.remove(file_path)
            elif os.path.isdir(file_path):
                shutil.rmtree(file_path)
    except Exception as e:
        print(f"删除时发生错误: {e}")


# 上传word或者markdown文件，返回题目列表
class UploadPaperFileServer(APIView):
    """
    接收docx文件，提取里面的题目，转换成列表返回
    """

    def post(self, request):
        """
        上传试卷docx文件，返回该文件包含的题目列表
        :param request: 单个docx文件
        :return: 成功返回题目列表
        """
        try:
            is_vocational = False
            is_answer_detach = False
            flag = True  # 表示题目列表是完整的
            received_file = request.FILES.get("upload_file", None)
            if not received_file:
                received_file = request.FILES.get("upload_file_vocational", None)
                if received_file:
                    is_vocational = True
                else:
                    is_answer_detach = True
                    received_file = request.FILES.get("upload_file_detach", None)
            # 先删除settings.TEMP_ROOT下的所有文件
            delete_all_in_md_folder(settings.TEMP_ROOT)
            file_name = os.path.join(settings.TEMP_ROOT, received_file.name)
            ff = open(file_name, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            flag = False
            error_message = '读取题目为空，请核对docx文件的格式'
            suffix = get_file_suffix(file_name)
            if not is_vocational:
                # 答案附题后
                if not is_answer_detach:
                    if suffix == 'zip':
                        flag, error_message, question_list, paper_info = read_markdown_file_attach(file_name)
                    else:
                        flag, error_message, question_list, paper_info = ReadDocx2QuestionListMarked.get_docx_text(
                            file_name)
                # 题目答案分离
                else:
                    if suffix == 'zip':
                        flag, error_message, question_list, paper_info = read_markdown_file_detach(file_name)
                    else:
                        flag, error_message, question_list, paper_info = ReadDocx2QuestionListMarked.get_docx_text_detach(
                            file_name)
            else:
                flag, error_message, question_list, paper_info = ReadDocx2QuestionListMarked.get_docx_text_vocational(
                    file_name)
            paper_and_question = {'paper_info': paper_info, 'question_list': question_list}
            if not flag and len(question_list) == 0:
                return Response(error_result(code=1099, results="错误", msg='试卷读取错误，请检查排版问题'))
            elif not flag and len(question_list) > 0:
                return Response(
                    error_result(code=201, msg=error_message, results=paper_and_question, total=len(question_list)))
            elif flag and len(question_list) > 0:
                return Response(error_result(code=200, msg='ok', results=paper_and_question, total=len(question_list)))
        except Exception as e:
            print("错误消息:", e)
            return Response(error_result(code=500, results="错误"))


class UploadGuidePaperFileServer(APIView):
    """
    接收docx或者pptx文件，存储到static/guide_paper/路径，并返回该路径
    """
    authentication_classes = []
    permission_classes = []

    def post(self, request):
        try:
            received_file = request.FILES.get("upload_file")
            file_prefix = received_file.name.split(".")[-1]
            if file_prefix.upper() not in ['DOCX', 'PPTX']:
                return Response(error_result(code=1099, msg='文件类型错误'))
            new_name = get_new_name() + '.' + file_prefix
            file_path = os.path.join(settings.GUIDE_ROOT, new_name)
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            return Response(error_result(code=200, results=settings.GUIDE_URL + new_name))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500, results="错误"))


# 组卷成docx文件或者其他格式
class QuestionList2DocxFile(APIView):
    """
        功能接收题目列表，写入docx文件，并返回文件的rul, 题目不持久化到数据库，上传前，题目已经持久化到数据库，现在建立多对多连接
        :param 题目列表
        :return 返回文件流
    """

    def post(self, request):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                print('now begin!')
                question_list = request.data["questionList"]
                if not question_list:
                    return Response(error_result(code=1099, msg="不能提交空白题目列表"))
                paper_information_dict = request.data.get("paperInformation")
                # 根据usage_type不同，选择不同的模板，即开头添加到内容不同，其中导学案比较复杂，考虑知识点，例题和课后练习、变式
                if not paper_information_dict:
                    title = '练习卷' + str(date.today())
                    paper_information_dict = {
                        'title': title,
                        'answer_position': 'detach',
                        'type_sorted': True,
                        'compose_file_type': 'docx',
                        'usage_type': 'exercise',
                        'return_file_type': 'docx'
                    }
                else:
                    if not paper_information_dict.get('answer_position'):
                        paper_information_dict['answer_position'] = 'detach'
                    if not paper_information_dict.get('type_sorted'):
                        paper_information_dict['type_sorted'] = False
                    if not paper_information_dict.get('title'):
                        paper_information_dict['title'] = '练习卷' + str(date.today())
                    if not paper_information_dict.get('compose_file_type'):
                        paper_information_dict['compose_file_type'] = 'docx'
                    if not paper_information_dict.get('usage_type'):
                        paper_information_dict['usage_type'] = 'exercise'
                    if not paper_information_dict.get('return_file_type'):
                        paper_information_dict['return_file_type'] = 'docx'
                print('params are ready')
                print('creat customer bank point!')
                # 分别获取本次被下载的所有者、题目被下载记录、总费用
                bank_point_change_this_list, download_this_new_list, sum_point = settle_bank_point_and_download_question(
                    question_list, self.request.user)
                bank_point_balance = User.objects.get(pk=request.user.pk).bank_point
                # 下载者的库币余额充足，下载总费用大于0，则进行下载
                if bank_point_balance < sum_point:
                    return Response(error_result(code=1099, results="库币不足，无法下载", msg="库币不足，无法下载"))
                elif bank_point_balance >= sum_point > 0:
                    # 获取公司账号, 下载者一次性付款给公司账号，然后公司再分发库币给被下载题目的所有者账号
                    username = Company.objects.first().president
                    user_company = User.objects.get(username=username)
                    create_customer_bank_point_log_change.delay(operator=request.user.pk,
                                                                receiver=user_company.pk,
                                                                point_amount=sum_point,
                                                                transaction_type=1,
                                                                remarks='下载题目')

                    # 异步创建题目下载后的交易记录， 同时触发模型信号，自动更新个人库币金额变化
                    for item in bank_point_change_this_list:
                        create_customer_bank_point_log_change.delay(receiver=item['receiver'],
                                                                    operator=user_company.pk,
                                                                    point_amount=item['point_amount'] * income_rate,
                                                                    remarks='被他人下载题目')
                print('create bank point log over!')
                print('create download log')
                # 异步创建题目下载记录，同时触发模型信号，自动更新题目被下载总量
                for item in download_this_new_list:
                    create_download_question_log(question=item, user=request.user)
                # file_path = question_list_2_docx_order_follow(question_list, paper_information_dict)
                print('starting compose!')
                print('question_list:', question_list)
                print('paper_info:', paper_information_dict)
                if paper_information_dict['return_file_type'] == 'docx':
                    file_path = write_question_list_into_docx(question_list=question_list,
                                                              paper_information=paper_information_dict)
                    extend = os.getcwd().replace('\\', '/')
                    file_path = extend + file_path
                    with open(file_path, 'rb') as f:
                        response = HttpResponse(f.read(), content_type='application/msword')
                        response['Content-Disposition'] = 'attachment; filename=download.docx'
                        return response
                elif paper_information_dict['return_file_type'] in ['pdf', 'tex', 'zip']:
                    result = write_questions_2_tex(question_list=question_list,
                                                   paper_information=paper_information_dict)
                    extension = paper_information_dict['return_file_type']
                    if result != 'error':
                        print('tex渲染结果：', result)
                        extend = os.getcwd().replace('\\', '/')
                        file_path = extend + result
                        with open(file_path, 'rb') as f:
                            response = HttpResponse(f.read(), content_type=f'application/{extension}')
                            response['Content-Disposition'] = f'attachment; filename=download.{extension}'
                            return response
                    else:
                        transaction.savepoint_rollback(sid)
                        return Response(error_result(code=1099))
                elif paper_information_dict['return_file_type'] in ['md', 'markdown']:
                    result = write_questions_2_markdown(question_list=question_list,
                                                        paper_information=paper_information_dict)
                    extension = paper_information_dict['return_file_type']
                    if result != 'error':
                        print('tex渲染结果：', result)
                        extend = os.getcwd().replace('\\', '/')
                        file_path = extend + result
                        with open(file_path, 'rb') as f:
                            response = HttpResponse(f.read(), content_type=f'application/{extension}')
                            response['Content-Disposition'] = f'attachment; filename=download.{extension}'
                            return response
                    else:
                        transaction.savepoint_rollback(sid)
                        return Response(error_result(code=1099))
                elif paper_information_dict['return_file_type'] in ['html']:
                    print('进入html渲染！')
                    result = write_questions_2_html(question_list=question_list,
                                                    paper_information=paper_information_dict)
                    extension = paper_information_dict['return_file_type']
                    if result != 'error':
                        print('tex渲染结果：', result)
                        extend = os.getcwd().replace('\\', '/')
                        file_path = extend + result
                        with open(file_path, 'rb') as f:
                            response = HttpResponse(f.read(), content_type=f'application/{extension}')
                            response['Content-Disposition'] = f'attachment; filename=download.docx'
                            return response
                    else:
                        transaction.savepoint_rollback(sid)
                        return Response(error_result(code=1099))
            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500))


class BaseViewSet(CustomNoQuestionsModelViewSet):
    queryset = Feedback
    serializer_classes = {}
    default_serializer_class = None
    filter_fields = ('title', 'owner')

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_classes.get(self.action, self.default_serializer_class)

    def list(self, request, *args, **kwargs):
        try:
            query_days = request.query_params.get('days')
            if not query_days:
                objs_server = self.get_queryset().objects.all()
            elif int(query_days) == 0:
                objs_server = self.get_queryset().objects.all()
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                objs_server = self.get_queryset().objects.filter(add_time__gt=past_date)
            page = self.paginate_queryset(objs_server)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(objs_server)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    def create(self, request, *args, **kwargs):
        try:
            owner = User.objects.get(pk=request.user.id)
            request.data['owner'] = owner
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid():
                print(serializer.errors)
                return Response(error_result(code=406, msg="data_error_text", results=serializer.errors))
            self.perform_create(serializer)
            return Response(error_result(code=200, msg='恭喜，提交反馈成功，感谢您的支持'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class QuestionFeedbackViewSet(BaseViewSet):
    queryset = QuestionFeedback
    serializer_classes = {
        'list': QuestionFeedbackModelSerializer,
        'retrieve': QuestionFeedbackModelSerializer,
        'create': QuestionFeedbackModelDeSerializer,
        'update': QuestionFeedbackModelSerializer,
    }
    default_serializer_class = QuestionFeedbackModelSerializer
    filter_fields = ('add_time', 'owner')

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_classes.get(self.action, self.default_serializer_class)


class GuidePaperFeedbackViewSet(BaseViewSet):
    queryset = GuidePaperFeedback
    filter_fields = ('add_time', 'owner')
    serializer_classes = {
        'list': GuidePaperFeedbackModelSerializer,
        'retrieve': GuidePaperFeedbackModelSerializer,
        'create': GuidePaperFeedbackModelDeSerializer,
        'update': GuidePaperFeedbackModelDeSerializer,
    }
    default_serializer_class = GuidePaperFeedbackModelSerializer

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_classes.get(self.action, self.default_serializer_class)


class FeedbackViewSet(BaseViewSet):
    queryset = Feedback
    serializer_classes = {
        'list': FeedbackModelSerializer,
        'retrieve': FeedbackModelSerializer,
        'create': FeedbackModelDeSerializer,
        'update': FeedbackModelSerializer,
    }
    default_serializer_class = FeedbackModelSerializer
    filter_fields = ('title', 'owner')

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_classes.get(self.action, self.default_serializer_class)

    # 查看自己名下的反馈
    @action(methods=['get'],
            detail=False)
    def get_feedback_myself(self, request):
        try:
            objs = self.get_queryset().objects.filter(owner__id=request.user.id)
            if not objs:
                return Response(error_result(code=1099, results={}, msg='没有记录'))
            serializer = self.get_serializer(objs, many=True)
            # print("赞助记录：", objs)
            return Response(error_result(code=200, results=serializer.data, total=len(objs)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class GetPaperMenuByDateAndGrade(APIView):
    """
    默认是获取在线且公开的试卷目录, 参数is_joint的值-1代表全部、1代表联考试卷、0代表自编试卷、空值代表全部
    """
    authentication_classes = []
    permission_classes = []

    def get(self, request):
        try:
            menu_cache_list = cache.get('GetPaperMenuByDateAndGrade')
            if menu_cache_list:
                return Response(error_result(code=200, results=menu_cache_list, msg='ok'))
            is_joint = request.query_params.get('is_joint')
            if not is_joint:
                paper_all_list = list(ExaminationPaper.objects.filter(Q(open_level='public') & Q(online=True)))
            else:
                if is_joint == '-1':
                    paper_all_list = list(ExaminationPaper.objects.filter(Q(open_level='public') & Q(online=True)))
                elif is_joint == '1' or is_joint == '0':
                    is_joint = True if is_joint == "1" else False
                    paper_all_list = list(
                        ExaminationPaper.objects.filter(Q(is_joint=is_joint) & Q(open_level='public') & Q(online=True)))
                else:
                    return Response(error_result(code=1099, msg="参数错误", results='No'))
            now = datetime.now()
            date_head = now.strftime("%Y-%m-%d")
            year_head = int(date_head[:4])
            month_head = int(date_head[5:7])

            date_tail = '2017-01-01'
            year_tail = int(date_tail[:4])
            id_number = 0
            menu_list = []
            year_item = {'id': id_number, 'name': str(year_head) + '年', 'children': []}
            id_number += 1
            paper_year_filter_list = list(
                filter(lambda paper: int(paper.publish_time.strftime(time_format)[:4]) == year_head, paper_all_list))
            for i in range(month_head, 0, -1):
                month_item = {'id': id_number, 'name': str(i) + '月', 'children': []}
                id_number += 1
                paper_month_list = list(
                    filter(lambda paper: int(paper.publish_time.strftime(time_format)[5:7]) == i,
                           paper_year_filter_list))
                for item in paper_month_list:
                    paper_item = {
                        'id': item.id,
                        'name': item.title,
                        'subject': item.subject.id,
                        'file': '' if item.file == '' else item.file,
                        'file_explain': '' if not item.file_explain else item.file_explain
                    }
                    month_item['children'].append(paper_item)
                year_item['children'].append(month_item)
            menu_list.append(year_item)
            for j in range(year_head - 1, year_tail - 1, -1):
                year_item = {'id': id_number, 'name': str(j) + '年', 'children': []}
                paper_year_filter_list = list(
                    filter(lambda paper: int(paper.publish_time.strftime(time_format)[:4]) == j, paper_all_list))
                for i in range(12, 0, -1):
                    month_item = {'id': id_number, 'name': str(i) + '月', 'children': []}
                    id_number += 1
                    paper_month_list = list(
                        filter(lambda paper: int(paper.publish_time.strftime(time_format)[5:7]) == i,
                               paper_year_filter_list))
                    for item in paper_month_list:
                        # print('type of file ', type(item.file))
                        # print('value', item.file)
                        paper_item = {
                            'id': item.id,
                            'name': item.title,
                            'subject': item.subject.id,
                            'file': '' if not item.file else item.file,
                            'file_explain': '' if not item.file_explain else item.file_explain
                        }
                        month_item['children'].append(paper_item)
                    year_item['children'].append(month_item)
                menu_list.append(year_item)
            # print('menu_list', menu_list)
            cache.set('GetPaperMenuByDateAndGrade', menu_list, 7 * 24 * 60 * 60)
            return Response(error_result(code=200, msg='OK', results=menu_list, total=len(menu_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, results={}))


class GetQuestionsByPaper(APIView):
    """
    根据试卷id，读取该试卷的所有题目
    """

    def get(self, request, format=None):
        try:
            paper_id = request.query_params.get('paper_id', None)
            # print('paper_id', paper_id)
            if paper_id is not None:
                paper_id = int(paper_id)
                paper = ExaminationPaper.objects.get(id=paper_id)
            else:
                paper = ExaminationPaper.objects.filter(subject__id=1).first()
                # print('first paper:', paper)
                paper_questions = QuestionOfPaper.objects.filter(paper__id=paper_id).values('question')
                if len(paper_questions) == 0:
                    paper = ExaminationPaper.objects.filter(subject__id=1).last()
                    # print('last paper:', paper)
            if not paper:
                return Response(error_result(code=1099, results='没有该id的试卷'))
            # print('paper:', paper)
            question_list = paper.questions.all()
            if not question_list:
                return Response(error_result(code=1099, results='该试卷没有题目!'))
            count = len(question_list)
            # 1. 实例化分页器对象
            page_obj = SmallPage()
            # 2. 使用自己配置的分页器调用分页方法进行分页
            page_data = page_obj.paginate_queryset(question_list, request)
            # 3. 序列化我们分页好的数据
            ser_obj = QuestionSerializer(page_data, many=True)
            # 4. 返回数据
            my_response_data = ser_obj.data
            if my_response_data:
                return Response(error_result(code=200, msg='OK', results=my_response_data, total=count))
        except Exception as E:
            print(read_paper_fail, E)
            return Response(error_result(code=500, msg=read_paper_fail, results="no"))


class GetAllUserWithQuestionIdAPIView(APIView):
    """
    根据question_id，读取该题目的所有收藏者
    """

    def get(self, request, format=None):
        try:
            question_id = request.query_params.get('question_id')
            question = Questions.objects.filter(id=question_id).first()
            if not question:
                return Response(error_result(code=1099, results=''))
            user_list = question.favorite.all()
            return Response(error_result(code=200, msg='OK', results=user_list))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg=read_paper_fail, results="no"))


class GetAllFavoriteQuestionsAPIView(APIView):
    """
    根据question_id，读取该题目的所有收藏者
    """

    def get(self, request, format=None):
        try:
            user = self.request.user
            question_list = user.FavoriteQuestion.all()
            question_data = QuestionSerializer(question_list, many=True).data
            return Response(error_result(code=200, msg='OK', results=question_data, total=len(question_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='读取题目列表失败', results="no"))


class GetAllFavoriteQuestionsByPageAPIView(APIView):
    """
    根据question_id，读取该题目的所有收藏者
    """

    def get(self, request, format=None):
        try:
            user = self.request.user
            question_list = user.FavoriteQuestion.all()
            count = len(question_list)
            # 1. 实例化分页器对象
            page_obj = SmallPage()
            # 2. 使用自己配置的分页器调用分页方法进行分页
            page_data = page_obj.paginate_queryset(question_list, request)
            # 3. 序列化我们分页好的数据
            # ser_obj = QuestionNoFavoritesSerializer(page_data, many=True).data
            ser_obj = QuestionSerializer(page_data, many=True).data
            # 4. 返回数据
            if count > 0:
                return Response(
                    error_result(code=200, msg='OK', results=ser_obj, total=count))
            else:
                return Response(error_result(code=1099, msg='该用户没有收藏题目', results="", total=0))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='读取题目列表失败', results="no"))


class UserFavoriteQuestionAPIView(APIView):
    """
    根据question_id和user_id进行增、删操作
    """

    def post(self, request):
        try:
            user = self.request.user
            question_id = request.data['question_id']
            user.FavoriteQuestion.add(question_id)
            question_list = user.FavoriteQuestion.all()
            data = QuestionDeserializer(question_list, many=True).data
            return Response(error_result(code=200, msg='增加收藏成功', results=data))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='增加收藏失败', results="no"))

    def delete(self, request):
        try:
            user = User.objects.get(pk=request.user.id)
            question_id = request.query_params['question_id']
            user.FavoriteQuestion.remove(question_id)
            question_list = user.FavoriteQuestion.all()
            count = len(question_list)
            # 1. 实例化分页器对象
            page_obj = SmallPage()
            # 2. 使用自己配置的分页器调用分页方法进行分页
            page_data = page_obj.paginate_queryset(question_list, request)
            # 3. 序列化我们分页好的数据
            ser_obj = QuestionNoFavoritesSerializer(page_data, many=True).data
            if count > 0:
                return Response(error_result(code=200, msg='取消收藏成功', results=ser_obj, total=count))
            else:
                return Response(error_result(code=1099, msg='该用户没有收藏题目', results="", total=0))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='取消收藏失败', results="", total=0))


class GeneralPaperApplicationModelViewSet(CustomNoQuestionsModelViewSet):
    """
    非联考卷审核
    """
    queryset = GeneralPaperApplication
    serializer_class = GeneralPaperApplicationModelSerializer
    filter_fields = ('status', 'publish_time')
    pagination_class = SmallPage
    ordering_fields = ('-last_edit_time',)

    def list(self, request, *args, **kwargs):
        try:
            query_days = request.query_params.get('days')
            if not query_days:
                applications = GeneralPaperApplication.objects.all()
            elif int(query_days) == 0:
                applications = GeneralPaperApplication.objects.all()
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                applications = GeneralPaperApplication.objects.filter(add_date__gt=past_date)
            page = self.paginate_queryset(applications)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return Response(error_result(code=200, results=serializer.data, total=len(applications)))
            serializer = self.get_serializer(applications, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    def create(self, request, *args, **kwargs):
        try:
            obj_id = request.data['id']
            user_id = request.user.id
            obj_server = self.get_queryset().objects.filter(Q(paper__pk=obj_id) & Q(user__id=user_id)).first()
            request.data['user'] = user_id
            request.data['paper'] = obj_id
            if not obj_server:
                request.data['status'] = 0
                result_serializer = self.get_serializer(data=request.data)
            else:
                paper_obj = ExaminationPaper.objects.filter(pk=obj_id).last()
                if not paper_obj.online and obj_server.status != 2:
                    request.data['last_edit_time'] = datetime.now()
                    request.data['status'] = 0
                    obj_modify = request.data
                    result_serializer = self.get_serializer(instance=obj_server, data=obj_modify, partial=True)
                elif obj_server.status == 2:
                    return Response(error_result(code=1022, msg=data_refuse, results='no'))
                elif paper_obj.online:
                    return Response(error_result(code=1022, msg="试卷已经上线，此操作没有必要", results='no'))
            if not result_serializer.is_valid():
                return Response(error_result(code=406, msg="data_error_text", results=result_serializer.errors))
            else:
                result_serializer.save()
                return Response(error_result(code=200, msg=apply_ok_result, results='ok'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))

    # 管理员审核
    def update(self, request, *args, **kwargs):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                application_id = kwargs['pk']
                application = GeneralPaperApplication.objects.filter(pk=application_id).first()
                if not application:
                    return Response(error_result(code=1021, msg=form_no_exit, results='no'))
                operate = request.data.get('operate')
                paper_id = request.data.get('paper_id')
                paper = ExaminationPaper.objects.filter(pk=paper_id).first()
                if operate == 'agree' and int(application.status) != 1:
                    application.status = 1
                    paper.online = True
                    question_list = paper.questions.all()
                    for item in question_list:
                        item.online = True
                        item.save()
                    paper.save()
                    application.save()
                    exists_cache = cache.get('GetPaperMenuByDateAndGrade')
                    if exists_cache:
                        cache.delete('GetPaperMenuByDateAndGrade')
                    create_send_notification.delay(user=paper.owner.id, content='你的试卷”' + paper.title + '“，已经通过审核')
                elif operate == 'reject' and int(application.status) != 2:
                    application.status = 2
                    paper.online = False
                    question_list = paper.questions.all()
                    for item in question_list:
                        item.online = False
                        item.save()
                    paper.save()
                    application.save()
                    create_send_notification.delay(user=paper.owner.id, content='你的试卷”' + paper.title + '”，已被管理员下架')
                else:
                    return Response(error_result(code=1022, msg=data_no_need_text, results='no'))
                # 根据时间范围返回申请表列表，默认缺省天数为7天
                query_days = request.data.get('days')
                if not query_days:
                    query_days = 7
                else:
                    query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                applications = self.get_queryset().objects.filter(add_date__gt=past_date)
                page = self.paginate_queryset(applications)
                serializer = self.get_serializer(page, many=True)
                print('正常执行完毕')
                return Response(error_result(code=200, results=serializer.data, total=len(applications)))
            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500, msg="错误", results='no'))

    # 手机号码和姓名搜索记录, 如果没有参数user，那就查询全部记录
    @action(methods=['get'], detail=False)
    def search(self, request):
        query_key = request.query_params.get('user')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(user__real_name__contains=query_key) | Q(user__phone__contains=query_key))
            if not applications:
                return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        else:
            applications = self.get_queryset().objects.all()
        page = self.paginate_queryset(applications)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))


class JointPaperApplicationModelViewSet(CustomNoQuestionsModelViewSet):
    """
    联考卷审核
    """
    queryset = JointPaperApplication
    serializer_class = JointPaperApplicationModelSerializer
    filter_fields = ('status', 'publish_time')
    pagination_class = SmallPage
    ordering_fields = ('-publish_time',)

    # 获取单个试卷
    def retrieve(self, request, *args, **kwargs):
        try:
            pk = kwargs.get('pk')
            query_obj = self.get_queryset().objects.filter(pk=pk).first()
            if not query_obj:
                return Response(error_result(code=1099))
            serializer = self.get_serializer(query_obj)
            return Response(error_result(code=200, results=serializer.data, msg='ok', total=1))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    # 获取所有的联考卷上线申请表，供管理员审核使用,如果不指定天数，则获取所有
    def list(self, request, *args, **kwargs):
        try:
            query_days = request.query_params.get('days')
            if not query_days:
                applications = JointPaperApplication.objects.all()
            elif int(query_days) == 0:
                applications = JointPaperApplication.objects.all()
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                applications = JointPaperApplication.objects.filter(add_date__gt=past_date)
            page = self.paginate_queryset(applications)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))

        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    # 普通用户创建自己的试卷上线申请，如果已经存在该试卷的申请，且之前的审核已经通过，则说明本次是再次修改的，则把状态更改为3
    def create(self, request, *args, **kwargs):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                obj_id = request.data['id']
                user_id = request.user.id
                obj_server = self.get_queryset().objects.filter(Q(paper__pk=obj_id) & Q(user__id=user_id))
                request.data['user'] = user_id
                request.data['paper'] = obj_id
                if not obj_server:
                    request.data['status'] = 0
                    result_serializer = self.get_serializer(data=request.data)
                else:
                    obj_server = obj_server.last()
                    if obj_server.paper.online:
                        return Response(error_result(code=1022, msg="你申请的试卷已经上线，此操作没必要", results='no'))
                    elif obj_server.status == 1:
                        request.data['last_edit_time'] = datetime.now()
                        request.data['status'] = 3
                        obj_modify = request.data
                        result_serializer = self.get_serializer(instance=obj_server, data=obj_modify, partial=True)
                    elif obj_server.status == 2:
                        return Response(error_result(code=1024, msg=data_refuse, results='no'))
                    elif obj_server.status == 3 or obj_server.status == 0:
                        return Response(error_result(code=1022, msg="你已经申请过该试卷，请联系管理员审核", results='no'))
                if not result_serializer.is_valid():
                    return Response(error_result(code=406, msg="data_error_text", results=result_serializer.errors))
                else:
                    result_serializer.save()
                    return Response(error_result(code=200, msg=apply_ok_result, results='ok'))
            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500, msg="错误", results='no'))

    # 管理员审核申请单，分为拒绝和通过
    def update(self, request, *args, **kwargs):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                application_id = kwargs['pk']
                application = self.get_queryset().objects.filter(pk=application_id)
                if not application:
                    return Response(error_result(code=1021, msg=form_no_exit, results='no'))
                else:
                    application = application.first()
                operate = request.data.get('operate')
                paper_id = request.data.get('paper_id')
                paper = ExaminationPaper.objects.filter(pk=paper_id).first()
                if operate == 'agree' and application.status != 1:
                    application.status = 1
                    paper.online = True
                    question_list = paper.questions.all()
                    for item in question_list:
                        item.online = True
                        item.save()
                    paper.save()
                    application.save()
                    exists_cache = cache.get('GetPaperMenuByDateAndGrade')
                    if exists_cache:
                        cache.delete('GetPaperMenuByDateAndGrade')
                    create_send_notification.delay(user=paper.owner.id, content='你的试卷”' + paper.title + '“，已经通过审核')
                elif operate == 'reject' and application.status != 2:
                    application.status = 2
                    paper.online = False
                    question_list = paper.questions.all()
                    for item in question_list:
                        item.online = False
                        item.save()
                    paper.save()
                    application.save()
                    create_send_notification.delay(user=paper.owner.id, content='你的试卷”' + paper.title + '”，已被管理员下架')
                else:
                    return Response(error_result(code=1022, msg=data_no_need_text, results='No'))
                # 根据时间范围返回申请表列表，默认缺省天数为7天
                query_days = request.data.get('days')
                if not query_days:
                    query_days = 7
                else:
                    query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                applications = self.get_queryset().objects.filter(add_date__gt=past_date)
                # if len(applications) == 0:
                #     return Response(error_result(code=1022, msg='返回时，没有查到申请表', results=[]))
                page = self.paginate_queryset(applications)
                serializer = self.get_serializer(page, many=True)
                return Response(error_result(code=200, results=serializer.data, total=len(applications)))

            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500, msg="错误", results='no'))

    # 手机号码和姓名搜索记录, 如果没有参数user，那就查询全部记录, 供管理员使用
    @action(methods=['get'], detail=False)
    def search(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(user__real_name__contains=query_key) | Q(
                    user__phone__contains=query_key | Q(title__contains=query_key))
            )
            if not applications:
                return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        else:
            applications = self.get_queryset().objects.all()
        page = self.paginate_queryset(applications)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        serializer = self.get_serializer(applications, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))

    # 筛选未处理或者再次申请未处理的的申请单
    @action(methods=['get'], detail=False)
    def untreated(self, request):
        try:
            query_days = request.query_params.get('days')
            if not query_days:
                applications = JointPaperApplication.objects.filter(Q(status=0) | Q(status=3))
            elif int(query_days) == 0:
                applications = JointPaperApplication.objects.filter(Q(status=0) | Q(status=3))
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                applications = JointPaperApplication.objects.filter(
                    Q(add_date__gt=past_date) & (Q(status=0) | Q(status=3)))
            page = self.paginate_queryset(applications)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


class PaperModelViewSet(CustomNoQuestionsModelViewSet):
    """
    联考卷审核
    list:查询请求者的录入试卷，参数days是几天之内的，默认是7天内
    """
    queryset = ExaminationPaper
    serializer_class = ExaminationPaperModelSerializer
    filter_fields = ('title', 'publish_time')
    pagination_class = SmallPage
    ordering_fields = ('-publish_time',)

    def retrieve(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        query_obj = self.get_queryset().objects.filter(pk=pk).first()
        if not query_obj:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        serializer = self.get_serializer(query_obj)
        return Response(error_result(code=200, results=serializer.data, msg='ok', total=1))

    def list(self, request, *args, **kwargs):
        try:
            user = request.user
            query_days = request.query_params.get('days')

            if not query_days:
                obj_list = self.get_queryset().objects.filter(owner__pk=user.id)
            elif int(query_days) <= 0:
                obj_list = self.get_queryset().objects.filter(owner__pk=user.id)
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                obj_list = self.get_queryset().objects.filter(Q(add_time__gt=past_date) & Q(owner__pk=user.id))
            page = self.paginate_queryset(obj_list)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    # 修改试卷的信息后，设置试卷下线，并将其包含的试题都下线,如果有该问题的上线申请单，需要修改其状态为未审核或者再次申请
    # 支持多张试卷批量处理
    def update(self, request, *args, **kwargs):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                request_data = request.data
                pk = kwargs.get('pk')
                modify_data_list = []
                server_data_list = []
                if pk and isinstance(request_data, dict):
                    obj_id = request_data.pop("id", None)
                    pk = int(pk)
                    if pk == obj_id:
                        request_data['last_edit_time'] = datetime.now()
                        request_data['online'] = False
                        modify_data_list.append(request_data)
                        data_server = self.get_queryset().objects.get(pk=pk)
                        question_list = data_server.questions.all()
                        for item in question_list:
                            item.online = False
                            item.save()
                        server_data_list.append(data_server)
                    else:
                        return Response(error_result(code=406, msg="id不一致", results="data_error_text"))
                # 批量修改试卷和相应的试题
                elif not pk and isinstance(request_data, list):
                    for item in request_data:
                        pk = item.pop("id")
                        item['last_edit_time'] = datetime.now()
                        server_obj = self.get_queryset().objects.get(pk=pk)
                        question_list = server_obj.questions.all()
                        for question_obj in question_list:
                            question_obj.online = False
                            question_obj.save()
                        server_data_list.append(server_obj)
                        modify_data_list.append(item)
                else:
                    return Response(error_result(code=406, msg="data_error_text", results="data_error_text"))
                result_serializer = self.get_serializer(instance=server_data_list, data=modify_data_list, many=True,
                                                        partial=True)
                if not result_serializer.is_valid(raise_exception=False):
                    print('序列化错误：', result_serializer.errors)
                    return Response(
                        error_result(code=406, msg=result_serializer.errors, results=result_serializer.errors))
                result_serializer.save()
                obj_list = self.get_queryset().objects.filter(owner__id=request.user.id)
                page = self.paginate_queryset(obj_list)
                serializer = self.get_serializer(page, many=True)
                # 修改该试卷的申请单为重新审批状态
                is_joint = True
                application_objs = JointPaperApplication.objects.filter(paper__id=pk)
                if len(application_objs) == 0:
                    application_objs = GeneralPaperApplication.objects.filter(paper__id=pk)
                    is_joint = False
                for application in application_objs:
                    modify_data_application = {'status': 3}
                    if is_joint:
                        result_serializer_application = JointPaperApplicationModelSerializer(instance=application,
                                                                                             data=modify_data_application,
                                                                                             partial=True)
                    else:
                        result_serializer_application = GeneralPaperApplicationModelSerializer(instance=application,
                                                                                               data=modify_data_application,
                                                                                               partial=True)
                    if not result_serializer_application.is_valid(raise_exception=False):
                        print('序列化错误：', result_serializer_application.errors)
                    else:
                        result_serializer_application.save()
                return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))

            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid=sid)
                return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    # 试卷标题搜索记录, 如果查询参数为空，那就查询全部记录，供管理员审核使用
    @action(methods=['get'], detail=False)
    def search(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            obj_list = self.get_queryset().objects.filter(
                Q(title__contains=query_key) | Q(owner__pk=request.user.id)
            )
            if not obj_list:
                return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        else:
            obj_list = self.get_queryset().objects.filter(owner__id=request.user.id)
        page = self.paginate_queryset(obj_list)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))
        serializer = self.get_serializer(obj_list, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))

    # 用户设置自己试卷的开放程度
    @action(methods=['patch'], detail=True)
    def setup_open_level(self, request, *args, **kwargs):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                obj_id = kwargs['pk']
                paper = self.get_queryset().objects.filter(Q(pk=obj_id) & Q(owner__id=request.user.id))
                if not paper:
                    return Response(error_result(code=1021, msg="不存在", results='no'))
                else:
                    paper = paper.first()
                open_level = request.data.get('open_level', None)
                if open_level not in ['public', 'private']:
                    return Response(error_result(code=1099, msg="关键字必须是public或private", results='No'))
                if open_level != paper.open_level:
                    paper.open_level = open_level
                    question_list = paper.questions.all()
                    for item in question_list:
                        item.open_level = open_level
                        item.save()
                    paper.save()
                else:
                    return Response(error_result(code=1022, msg=data_no_need_text, results='No'))
                # 根据时间范围返回，默认缺省天数为0天
                # query_days = request.data.get('days', None)
                # if not query_days:
                #     papers = self.get_queryset().objects.filter(owner__id=request.user.id)
                # else:
                #     query_days = int(query_days)
                #     current_date = date.today()
                #     past_date = current_date - timedelta(days=query_days)
                papers = self.get_queryset().objects.filter(owner__id=request.user.id)
                page = self.paginate_queryset(papers)
                serializer = self.get_serializer(page, many=True)
                return Response(error_result(code=200, results=serializer.data, total=len(papers)))
            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500, msg="服务器意外错误", results='no'))

    # 试卷标题搜索记录, 如果没有参数，那就查询全部记录，供普通用户使用，查询自己的试卷
    @action(methods=['get'], detail=False)
    def search_with_owner(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(paper__content__contains=query_key) & Q(user__id=request.user.id))
        else:
            applications = self.get_queryset().objects.filter(user__id=request.user.id)
        if not applications:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        page = self.paginate_queryset(applications)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))

    # 供普通用户使用，查询自己的试卷，可以设置天数
    @action(methods=['get'], detail=False)
    def days_and_owner(self, request):
        user = request.user
        query_days = request.query_params.get('days')
        if not query_days:
            obj_list = self.get_queryset().objects.filter(owner__pk=user.id)
        elif int(query_days) <= 0:
            obj_list = self.get_queryset().objects.filter(owner__pk=user.id)
        else:
            query_days = int(query_days)
            current_date = date.today()
            past_date = current_date - timedelta(days=query_days)
            obj_list = self.get_queryset().objects.filter(Q(add_time__gt=past_date) & Q(owner__pk=user.id))

        if not obj_list:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        page = self.paginate_queryset(obj_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))

    @action(methods=['get'], detail=False, url_path='subject')
    def subject(self, request):
        """获取某学科下的所有联考试卷，试卷已做的去掉（可选）"""
        try:
            subject_id = request.query_params['subject_id']
            has_writen = request.query_params['range']
            server_list = self.get_queryset().objects.filter(Q(subject__id=subject_id) &
                                                             Q(online=True) &
                                                             Q(open_level='public'))
            # 获取还没写过的试卷
            if has_writen != 'all':
                paper_list_has_writen = ChallengePaperOfUser.objects.all().values("paper")
                if paper_list_has_writen:
                    server_list = server_list.exclude(id__in=paper_list_has_writen)
            if not server_list:
                return Response(error_result(code=1099))
            page = self.paginate_queryset(server_list)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(server_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    @action(methods=['get'], detail=False)
    def filter_four_item(self, request):
        """4个条件筛选下的试题"""
        try:
            grade = request.query_params['grade']
            year = request.query_params['year']
            stage = request.query_params['stage']
            order = request.query_params['order']
            key_words = request.query_params['key_words']
            if grade == 'all' and stage == 'all' and key_words == '' and year == 'all':
                question_service_list = Questions.objects.all()
            else:
                filter_list = []
                paper_list_query = QuestionOfPaper.objects
                if grade != 'all':
                    filter_list.append(grade)
                    # paper_list_query = paper_list_query.filter(title__contains=grade)
                if stage != 'all':
                    filter_list.append(stage)
                    # paper_list_query = paper_list_query.filter(title__contains=stage)
                if year != 'all':
                    filter_list.append(year)
                    # paper_list_query = paper_list_query.filter(title__contains=year)
                if key_words != '':
                    filter_list.append(key_words)
                    # paper_list_query = paper_list_query.filter(title__contains=key_words)
                if len(filter_list) == 1:
                    paper_list_query = paper_list_query.filter(Q(paper__title__contains=filter_list[0]))
                elif len(filter_list) == 2:
                    paper_list_query = paper_list_query.filter(Q(paper__title__contains=filter_list[0]) &
                                                               Q(paper__title__contains=filter_list[1]))
                elif len(filter_list) == 3:
                    paper_list_query = paper_list_query.filter(Q(paper__title__contains=filter_list[0]) &
                                                               Q(paper__title__contains=filter_list[1]) &
                                                               Q(paper__title__contains=filter_list[2]))
                elif len(filter_list) == 4:
                    paper_list_query = paper_list_query.filter(Q(paper__title__contains=filter_list[0]) &
                                                               Q(paper__title__contains=filter_list[1]) &
                                                               Q(paper__title__contains=filter_list[2]) &
                                                               Q(paper__title__contains=filter_list[3]))
                if not paper_list_query:
                    return Response(error_result(code=1099, msg='没有符合条件的试卷'))
                paper_id_list = convert_dic_list(paper_list_query.values('id', 'question'), key='question')
                question_service_list = Questions.objects.filter(id__in=paper_id_list)
            if len(question_service_list) == 0:
                return Response(error_result(code=1098, msg='没有符合条件的试题'))
            if order != 'default' and order in ['publish_time', '-publish_time']:
                question_service_list = question_service_list.order_by(order)
            page = self.paginate_queryset(question_service_list)
            serializer = QuestionSerializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(question_service_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    @action(methods=['get'], detail=False)
    def get_paper_by_filter(self, request):
        """4个条件筛选下的试题"""
        try:
            grade = request.query_params['grade']
            key_words = request.query_params['key_words']
            stage = request.query_params['stage']
            year = request.query_params['year']
            order = request.query_params['order']
            four_default = False
            if grade == 'all' and stage == 'all' and key_words == '' and year == 'all':
                four_default_cache = cache.get('four_default_all_paper')
                if four_default_cache:
                    return Response(error_result(code=200, results=four_default_cache, total=len(four_default_cache)))
                paper_list_query = self.get_queryset().objects.all()
                four_default = True
            else:
                filter_list = []
                paper_list_query = self.get_queryset().objects
                if grade != 'all':
                    filter_list.append(grade)
                    # paper_list_query = paper_list_query.filter(title__contains=grade)
                if stage != 'all':
                    filter_list.append(stage)
                    # paper_list_query = paper_list_query.filter(title__contains=stage)
                if year != 'all':
                    filter_list.append(year)
                    # paper_list_query = paper_list_query.filter(title__contains=year)
                if key_words != '':
                    filter_list.append(key_words)
                    # paper_list_query = paper_list_query.filter(title__contains=key_words)
                if len(filter_list) == 1:
                    paper_list_query = paper_list_query.filter(Q(title__contains=filter_list[0]))
                elif len(filter_list) == 2:
                    paper_list_query = paper_list_query.filter(Q(title__contains=filter_list[0]) &
                                                               Q(title__contains=filter_list[1]))
                elif len(filter_list) == 3:
                    paper_list_query = paper_list_query.filter(Q(title__contains=filter_list[0]) &
                                                               Q(title__contains=filter_list[1]) &
                                                               Q(title__contains=filter_list[2]))
                elif len(filter_list) == 4:
                    paper_list_query = paper_list_query.filter(Q(title__contains=filter_list[0]) &
                                                               Q(title__contains=filter_list[1]) &
                                                               Q(title__contains=filter_list[2]) &
                                                               Q(title__contains=filter_list[3]))
                if not paper_list_query:
                    return Response(error_result(code=1099, msg='没有符合条件的试卷'))

            if len(paper_list_query) == 0:
                return Response(error_result(code=1098, msg='没有符合条件的试卷'))
            if order != 'default' and order in ['publish_time', '-publish_time']:
                paper_list_query = paper_list_query.order_by(order)
            page = self.paginate_queryset(paper_list_query)
            serializer = self.get_serializer(page, many=True)
            if four_default and not cache.get('four_default_all_paper'):
                cache.set('four_default_all_paper', serializer.data, 7 * 3600)
            return Response(error_result(code=200, results=serializer.data, total=len(paper_list_query)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))


class QuestionOnlineApplicationModelViewSet(CustomNoQuestionsModelViewSet):
    """
    试题上线审核
    """
    queryset = QuestionOnlineApplication
    serializer_class = QuestionOnlineApplicationModelSerializer
    filter_fields = ('status', 'publish_time')
    pagination_class = SmallPage
    ordering_fields = ('-last_edit_time',)

    # 返回所有的申请单,且审核状态为未审核和再次审核的，用于管理员审核
    def list(self, request, obj_server_temp=None, *args, **kwargs):
        try:
            obj_server = obj_server_temp
            query_days = request.query_params.get('days')
            if not query_days:
                obj_server = self.get_queryset().objects.filter(Q(status=0) | Q(status=3))
            elif int(query_days) == 0:
                obj_server = self.get_queryset().objects.filter(Q(status=0) | Q(status=3))
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                obj_server = self.get_queryset().objects.filter(last_edit_time__gt=past_date)
            page = self.paginate_queryset(obj_server)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(obj_server)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    # 普通用户创建上线申请单，如果已经存在该用户的上线申请单且之前审核通过，说明本次是再次修改题目内容，把状态更改为3
    def create(self, request, *args, **kwargs):
        try:
            obj_id = request.data['id']
            user_id = request.user.id
            obj_server = self.get_queryset().objects.filter(Q(question__pk=obj_id) & Q(user__id=user_id))
            request.data['user'] = user_id
            request.data['question'] = obj_id
            if not obj_server:
                request.data['status'] = 0
                result_serializer = self.get_serializer(data=request.data)
            else:
                obj_server = obj_server.last()
                if obj_server.question.online:
                    return Response(error_result(code=1022, msg="你申请的试题已经上线，此操作没必要", results='no'))
                if obj_server.status == 1:
                    request.data['last_edit_time'] = datetime.now()
                    request.data['status'] = 3
                    obj_modify = request.data
                    result_serializer = self.get_serializer(instance=obj_server, data=obj_modify, partial=True)
                elif obj_server.status == 2:
                    return Response(error_result(code=1024, msg=data_refuse, results='no'))
                elif obj_server.question.online:
                    return Response(error_result(code=1022, msg="试题已经上线，此操作没有必要", results='no'))
                elif obj_server.status == 3 or obj_server.status == 0:
                    return Response(error_result(code=1022, msg="你已经申请过该试题上线，请联系管理员审核", results='no'))
            if not result_serializer.is_valid():
                return Response(error_result(code=406, msg="data_error_text", results=result_serializer.errors))
            else:
                result_serializer.save()
                return Response(error_result(code=200, msg=apply_ok_result, results='ok'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))

    """管理员审核申请单"""

    def update(self, request, *args, **kwargs):
        try:
            application_id = kwargs['pk']
            application = self.get_queryset().objects.filter(pk=application_id)
            if not application:
                return Response(error_result(code=1021, msg=form_no_exit, results='no'))
            operate = request.data.get('operate')
            application = application.first()
            obj = application.question
            if operate == 'agree' and application.status != 1:
                application.status = 1
                obj.online = True
                obj.save()
                application.save()
            elif operate == 'reject' and application.status != 2:
                application.status = 2
                obj.online = False
                obj.save()
                application.save()
            else:
                return Response(error_result(code=1022, msg="重复操作，此操作没必要", results='No'))
            # 返回所有未审核的申请表
            applications = self.get_queryset().objects.filter(Q(status=0) | Q(status=3))
            page = self.paginate_queryset(applications)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return Response(error_result(code=200, results=serializer.data, total=len(applications)))
            serializer = self.get_serializer(applications, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))

    # 手机号码和姓名搜索记录, 如果没有参数user，那就查询全部记录, 供普通用户查询自己的录入试题
    @action(methods=['get'], detail=False)
    def search_with_owner(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(question__content__contains=query_key) & Q(user__id=request.user.id))
        else:
            applications = self.get_queryset().objects.filter(user__id=request.user.id)
        if not applications:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        page = self.paginate_queryset(applications)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        serializer = self.get_serializer(applications, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))

    # 搜索手机号码或者真名或者题目内容，未审核或者再次审核的题目, 供管理员使用
    @action(methods=['get'], detail=False)
    def search(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(question__content__contains=query_key) | Q(user__phone=query_key) | Q(user__real_name=query_key))
        else:
            applications = self.get_queryset().objects.all()
        if not applications:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        page = self.paginate_queryset(applications)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))


class QuestionPrivateOnlineApplicationModelViewSet(CustomNoQuestionsModelViewSet):
    """
    试题上线审核
    """
    queryset = QuestionPrivateOnlineApplication
    serializer_class = QuestionPrivateOnlineApplicationModelSerializer
    filter_fields = ('status', 'publish_time')
    pagination_class = SmallPage
    ordering_fields = ('-last_edit_time',)

    # 返回所有的申请单,且审核状态为未审核和再次审核的，用于管理员审核
    def list(self, request, obj_server_temp=None, *args, **kwargs):
        try:
            obj_server = obj_server_temp
            query_days = request.query_params.get('days')
            if not query_days:
                obj_server = self.get_queryset().objects.filter(Q(status=0) | Q(status=3))
            elif int(query_days) == 0:
                obj_server = self.get_queryset().objects.filter(Q(status=0) | Q(status=3))
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                obj_server = self.get_queryset().objects.filter(last_edit_time__gt=past_date)
            page = self.paginate_queryset(obj_server)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(obj_server)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))

    # 普通用户创建上线申请单，如果已经存在该用户的上线申请单且之前审核通过，说明本次是再次修改题目内容，把状态更改为3
    def create(self, request, *args, **kwargs):
        try:
            obj_id = request.data['id']
            user_id = request.user.id
            obj_server = self.get_queryset().objects.filter(Q(question__pk=obj_id) & Q(user__id=user_id))
            request.data['user'] = user_id
            request.data['question'] = obj_id
            if not obj_server:
                request.data['status'] = 0
                result_serializer = self.get_serializer(data=request.data)
            else:
                obj_server = obj_server.last()
                if obj_server.question.online:
                    return Response(error_result(code=1022, msg="你申请的试题已经上线，此操作没必要", results='no'))
                if obj_server.status == 1:
                    request.data['last_edit_time'] = datetime.now()
                    request.data['status'] = 3
                    obj_modify = request.data
                    result_serializer = self.get_serializer(instance=obj_server, data=obj_modify, partial=True)
                elif obj_server.status == 2:
                    return Response(error_result(code=1024, msg=data_refuse, results='no'))
                elif obj_server.question.online:
                    return Response(error_result(code=1022, msg="试题已经上线，此操作没有必要", results='no'))
                elif obj_server.status == 3 or obj_server.status == 0:
                    return Response(error_result(code=1022, msg="你已经申请过该试题上线，请联系管理员审核", results='no'))
            if not result_serializer.is_valid():
                return Response(error_result(code=406, msg="data_error_text", results=result_serializer.errors))
            else:
                result_serializer.save()
                return Response(error_result(code=200, msg=apply_ok_result, results='ok'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))

    """管理员审核申请单"""

    def update(self, request, *args, **kwargs):
        try:
            application_id = kwargs['pk']
            application = self.get_queryset().objects.filter(pk=application_id)
            if not application:
                return Response(error_result(code=1021, msg=form_no_exit, results='no'))
            operate = request.data.get('operate')
            application = application.first()
            obj = application.question
            if operate == 'agree' and application.status != 1:
                application.status = 1
                obj.online = True
                obj.save()
                application.save()
            elif operate == 'reject' and application.status != 2:
                application.status = 2
                obj.online = False
                obj.save()
                application.save()
            else:
                return Response(error_result(code=1022, msg="重复操作，此操作没必要", results='No'))
            # 返回所有未审核的申请表
            applications = self.get_queryset().objects.filter(Q(status=0) | Q(status=3))
            page = self.paginate_queryset(applications)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return Response(error_result(code=200, results=serializer.data, total=len(applications)))
            serializer = self.get_serializer(applications, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))

    # 手机号码和姓名搜索记录, 如果没有参数user，那就查询全部记录, 供普通用户查询自己的录入试题
    @action(methods=['get'], detail=False)
    def search_with_owner(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(question__content__contains=query_key) & Q(user__id=request.user.id))
        else:
            applications = self.get_queryset().objects.filter(user__id=request.user.id)
        if not applications:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        page = self.paginate_queryset(applications)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        serializer = self.get_serializer(applications, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))

    # 搜索手机号码或者真名或者题目内容，未审核或者再次审核的题目, 供管理员使用
    @action(methods=['get'], detail=False)
    def search(self, request):
        query_key = request.query_params.get('query_key')
        if query_key:
            applications = self.get_queryset().objects.filter(
                Q(question__content__contains=query_key) | Q(user__phone=query_key) | Q(user__real_name=query_key))
        else:
            applications = self.get_queryset().objects.all()
        if not applications:
            return Response(error_result(code=1099, msg=data_query_fail, results='no'))
        page = self.paginate_queryset(applications)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(applications)))


class PaperFormatDownLoadView(APIView):
    """
    下载试卷录入的格式帮助文档
    """

    def get(self, request):
        file_path = './static/files/templates/file.docx'
        try:
            with open(file_path, 'rb') as f:
                response = HttpResponse(f.read(), content_type='application/msword')
                response['Content-Disposition'] = 'attachment; filename=file.docx'
                return response
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


class HelloWorldApiView(APIView):
    """
    下载试卷录入的格式帮助文档
    """
    authentication_classes = []
    permission_classes = []

    def get(self, request):
        return Response(error_result(code=200, msg="yes", results='hello world!'))


class Pdf2DocxAPIView(APIView):
    """
    post方式接收前端提交到pdf文件，保存到/static/file文件夹中，并把该文件的url提交给度慧服务器，从而获得度慧的token，该token返回给前端
    get方式，参数为token,客户端轮询docx转换结果
    """

    def get(self, request, format=None):
        try:
            result_docx_token = request.query_params.get('docx_token')
            result_two = pdf2word_step2(result_docx_token)
            result_two = eval(result_two.decode('utf-8'))
            if not result_two:
                return Response(error_result(code=201, msg=file_upload_ok, results=result_docx_token))
            if result_two['code'] != 10000:
                return Response(error_result(code=1099, msg='token错误', results='no'))
            if result_two['result']['status'] == 'Doing':
                return Response(error_result(code=201, msg=file_upload_ok, results=result_docx_token))
            if result_two['result']['status'] == 'Done':
                return Response(error_result(code=200, msg='转换成功，请及时下载文件', results=result_two['result']['fileurl']))
            return Response(error_result(code=1099, msg='服务器意外错误，请联系管理员', results="no"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    def post(self, request):
        try:
            received_file = request.FILES.get("upload_file")
            file_path = os.path.join(settings.FILE_ROOT, received_file.name)
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            file_url = settings.SERVER_IP + settings.FILE_URL + received_file.name
            result_one = pdf2word_step1(file_url)
            result_one = eval(result_one.decode('utf-8'))
            # print(result_one['code'])
            if not result_one:
                return Response(error_result(code=1099, msg='上传失败', results='no'))
            if result_one['code'] != 10000:
                return Response(error_result(code=1099, msg='参数错误', results='no'))
            result_docx_token = result_one['result']['token']
            time.sleep(13)
            result_two = pdf2word_step2(result_docx_token)
            result_two = eval(result_two.decode('utf-8'))
            # print(result_two)
            if not result_two:
                return Response(error_result(code=201, msg=file_upload_ok, results=result_docx_token))
            if result_two['code'] != 10000:
                return Response(error_result(code=1099, msg='token错误', results='no'))
            if result_two['result']['status'] == 'Doing':
                return Response(error_result(code=201, msg=file_upload_ok, results=result_docx_token))
            if result_two['result']['status'] == 'Done':
                # print('token', result_two['result']['fileurl'])
                return Response(error_result(code=200, msg='转换成功，请及时下载文件', results=result_two['result']['fileurl']))
            return Response(error_result(code=1099, msg='服务器意外错误，请联系管理员', results="no"))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class Pdf2FileAPIView(APIView):
    """
    post方式接收前端提交到pdf文件，保存到/static/file文件夹中，并把该文件的url提交给度慧服务器，从而获得度慧的token，该token返回给前端
    """
    authentication_classes = []
    permission_classes = []

    def post(self, request):
        try:
            folder = settings.IMAGE_TEMP_URL
            received_file_type = 'docx'
            received_file = request.FILES.get("upload_file", None)
            if not received_file:
                received_file = request.FILES.get("md", None)
                if not received_file:
                    received_file = request.FILES.get("tex", None)
                    if not received_file:
                        received_file = request.FILES.get("docx", None)
                        if not received_file:
                            return Response(error_result(code=500))
                        else:
                            file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + folder + received_file.name
                    else:
                        received_file_type = 'tex'
                        file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + folder + received_file.name
                else:
                    received_file_type = 'md'
                    file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + folder + received_file.name
            else:
                file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + folder + received_file.name
            print('file_path：', file_path)
            ff = open(file_path, 'wb')
            for chunk in received_file.chunks():
                ff.write(chunk)
            ff.close()
            # file_url = settings.SERVER_IP + settings.IMAGE_TEMP_URL + received_file.name
            print('folder:', folder)
            result_flag, result_path = Pdf2File.main(folder, file_path, received_file_type)
            if not result_flag:
                return Response(error_result(code=1099, msg='token错误', results='no'))
            else:
                url = settings.SERVER_IP + result_path
                return Response(error_result(code=200, msg='转换成功，请及时下载文件', results=url))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


# 百度官方教育试卷ocr
class ImageOcrAPIView(APIView):
    def post(self, request):
        try:
            received_file = request.FILES.get("images")
            if not received_file:
                return Response(error_result(code=1099, msg='上传失败', results='no'))
            images_url, images_path = SaveImage.save_image_temp(received_file)
            access_token = cache.get('ocr_access_token')
            if not access_token:
                access_token = BaiduOcr.get_baidu_ocr_access_token()
            if not access_token:
                return Response(error_result(code=1099, msg='token fail!', results='no'))
            ocr_results_string = BaiduOcr.get_baidu_ocr_result(file=images_path, access_token=access_token)
            if ocr_results_string == '':
                return Response(error_result(code=1099, msg='ocr result is null!', results={}))
            # print(ocr_results_string)
            return Response(error_result(code=200, msg='转换成功', results=ocr_results_string))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class GuidePaperModelViewSet(CustomNoQuestionsModelViewSet):
    """
    导学案和课件
    """
    queryset = GuidePaper
    serializer_class = GuidePaperSerializer
    filter_fields = ('title', 'add_date')
    pagination_class = SmallPage
    ordering_fields = ('order',)

    # 修改导学案的信息后，设置导学案下线，需要修改其状态为未审核或者再次申请
    def update(self, request, *args, **kwargs):
        try:
            request_data = request.data
            pk = kwargs.get('pk')
            request_data.pop('id', None)
            modify_data = request_data
            server_data = self.get_queryset().objects.filter(pk=pk)
            if not server_data:
                return Response(error_result(code=1099, msg='不存在更新对象', results='no'))
            server_data = server_data.first()
            result_serializer = self.get_serializer(instance=server_data, data=modify_data, partial=True)
            if not result_serializer.is_valid(raise_exception=False):
                print(result_serializer.errors)
                return Response(error_result(code=406, msg=result_serializer.errors, results='no'))
            result_serializer.save()
            return Response(error_result(code=200, msg=data_success_text))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    def create(self, request, *args, **kwargs):
        try:
            request.data['online'] = False
            request.data['owner'] = request.user.pk
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, msg="参数错误", results='no'))
            self.perform_create(serializer)
            # 创建导学案成功后，同时创建申请上线表单(所有）
            one_minute = datetime.now() - timedelta(minutes=1)
            paper_server = GuidePaper.objects.filter(Q(owner__pk=request.user.pk) & Q(add_time__lte=one_minute))
            if not paper_server:
                return Response(error_result(code=201, msg=data_upload_ok_online_fail_text, results='yes'))
            paper_server = paper_server.last()
            application_dic = {'paper': paper_server.pk, 'user': request.user.pk, 'status': 0}
            application_serializer = GuidePaperApplicationModelSerializer(data=application_dic)
            if not application_serializer.is_valid():
                return Response(error_result(code=201, msg=data_upload_ok_online_fail_text, results='yes'))
            application_serializer.save()
            return Response(error_result(code=200, msg=data_upload_ok_online_fail_text, results='yes'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    def list(self, request, *args, **kwargs):
        """查询所有学思案，没有限制"""
        try:
            obj_list = self.get_queryset().objects.all()
            page = self.paginate_queryset(obj_list)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 根据知识点id去过滤该知识点下的所有导学案和课件，并且在线和公开
    @action(methods=['get'], detail=False)
    def knowledgepoint(self, request):
        query_key = request.query_params.get('id')
        paper_type = request.query_params.get('paper_type')
        objs = None
        if query_key is not None and paper_type is not None and int(query_key) * int(paper_type) > 0:
            objs = self.get_queryset().objects.filter(
                Q(knowledgepoint__id=int(query_key)) & Q(online=True) & Q(open_level='public') &
                Q(paper_type=int(paper_type)))
        elif query_key is not None and int(query_key) > 0:
            objs = self.get_queryset().objects.filter(
                Q(knowledgepoint__id=int(query_key)) & Q(online=True) & Q(open_level='public'))
        elif paper_type is not None and int(paper_type) > 0:
            objs = self.get_queryset().objects.filter(
                Q(paper_type=int(paper_type)) & Q(online=True) & Q(open_level='public'))
        else:
            objs = self.get_queryset().objects.filter(Q(online=True) & Q(open_level='public'))
        if not objs:
            return Response(error_result(code=1099, msg=data_query_fail, results=[]))
        page = self.paginate_queryset(objs)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(objs)))

    # 所有导学案和课件，并且在线和公开
    @action(methods=['get'], detail=False)
    def all_online_public(self, request):
        try:
            obj_list = self.get_queryset().objects.filter(Q(online=True) & Q(open_level='public'))
            page = self.paginate_queryset(obj_list)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 自己名下的导学案/课件,带天数为参数，如果不带天数，或天数为0，则全选自己名下的所有
    @action(methods=['get'], detail=False)
    def own_guide_paper(self, request):
        try:
            query_days = request.query_params.get('days')
            obj_server = self.get_queryset().objects.filter(owner__pk=request.user.pk)
            if query_days:
                if int(query_days) > 0:
                    current_date = date.today()
                    past_date = current_date - timedelta(days=int(query_days))
                    obj_server = self.get_queryset().objects.filter(Q(owner__pk=request.user.pk) &
                                                                    Q(last_edit_time__gt=past_date))
            page = self.paginate_queryset(obj_server)
            serializer = self.get_serializer(page, many=True)
            if not serializer:
                return Response(error_result(code=1099, results='no', total=0))
            return Response(error_result(code=200, results=serializer.data, total=len(obj_server)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 搜索自己名下的导学案/课件, 可以带type筛选导学案或者课件的标题内容
    @action(methods=['get'], detail=False)
    def search_own_guide_paper(self, request):
        try:
            query_key = request.query_params.get('query_key')
            if query_key:
                obj_list = self.get_queryset().objects.filter(
                    Q(owner__pk=request.user.pk) & Q(title__contains=query_key))
            else:
                obj_list = self.get_queryset().objects.filter(owner__pk=request.user.pk)
            page = self.paginate_queryset(obj_list)
            serializer = self.get_serializer(page, many=True)
            if not serializer:
                return Response(error_result(code=1099, results='no', total=0))
            return Response(error_result(code=200, results=serializer.data, total=len(obj_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 设置自己名下的导学案/课件, 需要携带参数
    @action(methods=['patch'], detail=True)
    def set_own_guide_paper_open_level(self, request, *args, **kwargs):
        try:
            obj_id = kwargs['pk']
            paper = self.get_queryset().objects.filter(Q(pk=obj_id) & Q(owner__id=request.user.id))
            if not paper:
                return Response(error_result(code=1021, msg="你没有该导学案/课件", results='no'))
            else:
                paper = paper.first()
            open_level = request.data.get('open_level', None)
            if open_level not in ['public', 'private']:
                return Response(error_result(code=1099, msg="关键字必须是public或private", results='No'))
            if open_level != paper.open_level:
                paper.open_level = open_level
                paper.save()
            else:
                return Response(error_result(code=1022, msg=data_no_need_text, results='No'))
            return Response(error_result(code=200, msg='修改成功'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class Course4GuidePaperViewSet(CustomNoQuestionsModelViewSet):
    authentication_classes = []
    permission_classes = []
    queryset = Course.objects.all()
    serializer_class = CourseContainGuidePaperSerializer


class GuidePaperApplicationModelViewSet(CustomNoQuestionsModelViewSet):
    """
    导学案和课件申请上线表单
    """
    queryset = GuidePaperApplication
    serializer_class = GuidePaperApplicationModelSerializer
    filter_fields = ('status', 'add_date')
    pagination_class = SmallPage
    ordering_fields = ('add_date',)

    def create(self, request, *args, **kwargs):
        try:
            guide_paper_pk = request.data['paper']
            request.data['user'] = request.user.pk
            request.data['status'] = 0
            guide_paper_server = GuidePaper.objects.filter(pk=guide_paper_pk)
            if not guide_paper_server:
                return Response(error_result(code=1099, msg="导学案id错误", results='no'))
            guide_paper_server = guide_paper_server.first()
            if request.user.pk != guide_paper_server.owner.pk:
                return Response(error_result(code=1099, msg="该学思案不属于你，无法给你创建申请", results='no'))
            application = self.get_queryset().objects.filter(paper__id=guide_paper_pk)
            # 如果该导学案已经存在申请单，则直接修改表单
            if application:
                application = application.first()
                if application.status != 2:
                    return Response(error_result(code=1021, msg="管理员未处理或者同意，此操作没有必要", results='no'))
                application.status = 3
                application.save()
                return Response(error_result(code=200, results='yes', msg='申请成功'))
            # 如果不存在申请表单，则创建
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, msg="参数错误", results='no'))
            self.perform_create(serializer)
            return Response(error_result(code=200, results='yes', msg='申请成功'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))

    # 获取所有的联考卷上线申请表，供管理员审核使用,如果不指定天数，则获取所有
    def list(self, request, *args, **kwargs):
        try:
            query_days = request.query_params.get('days')
            if not query_days:
                applications = self.get_queryset().objects.all()
            elif int(query_days) == 0:
                applications = self.get_queryset().objects.all()
            else:
                query_days = int(query_days)
                current_date = date.today()
                past_date = current_date - timedelta(days=query_days)
                applications = self.get_queryset().objects.filter(add_date__gt=past_date)
            page = self.paginate_queryset(applications)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))

        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg=data_parameters_error, results='no'))
        # 管理员审核申请单，分为拒绝和通过

    def update(self, request, *args, **kwargs):
        try:
            application_id = kwargs['pk']
            application = self.get_queryset().objects.filter(pk=application_id)
            if not application:
                return Response(error_result(code=1021, msg=form_no_exit, results='no'))
            else:
                application = application.first()
            operate = request.data.get('operate')
            paper_id = request.data.get('paper_id')
            paper = GuidePaper.objects.filter(pk=paper_id).first()
            if operate == 'agree' and application.status != 1:
                application.status = 1
                paper.online = True
                paper.save()
                application.save()
            elif operate == 'reject' and application.status != 2:
                application.status = 2
                application.detail = request.data.get('detail')
                paper.online = False
                paper.save()
                application.save()
            else:
                return Response(error_result(code=1022, msg=data_no_need_text, results='No'))
            # 根据时间范围返回申请表列表，默认缺省天数为7天
            query_days = request.data.get('days')
            if not query_days:
                query_days = 7
            else:
                query_days = int(query_days)
            current_date = date.today()
            past_date = current_date - timedelta(days=query_days)
            applications = self.get_queryset().objects.filter(add_date__gt=past_date)
            page = self.paginate_queryset(applications)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return Response(error_result(code=200, results=serializer.data, total=len(applications)))
            serializer = self.get_serializer(applications, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(applications)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


class ChatCompletionAPIView(APIView):
    """
    post:
    发送接收rand1 rand2和验证码，检测合格后，返回是否注册成
    """
    authentication_classes = []
    permission_classes = []

    def post(self, request):
        try:
            url, headers, request_body = chat_completion()
            # 获取客户的留言
            client_text = request.data['text']
            # 将当次输入内容作为用户的一轮对话添加到messages
            request_body["messages"].append(
                {"sender_type": "USER", "sender_name": "小明", "text": client_text}
            )
            response = requests.post(url, headers=headers, json=request_body)
            reply = response.json()["reply"]
            reply = str(reply)
            # print(f"reply: {reply}")
            # 将当次的ai回复内容加入messages
            request_body["messages"].extend(response.json()["choices"][0]["messages"])
            # print("准备发给前端")
            return Response(error_result(code=200, results=reply))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


def select_query(pk, index):
    queryset_list = [Course.objects.filter(subject__pk=pk), Chapter.objects.filter(course__pk=pk),
                     Section.objects.filter(chapter__pk=pk), KnowledgePoint.objects.filter(section__pk=pk),
                     KnowledgePoint.objects.filter(pk=pk)]
    return queryset_list[index]


# 根据学科获取相应的知识点
class GetKnowledgepointBySubjectApiView(APIView):

    def get(self, request):
        try:
            pk = request.query_params.get('pk', None)
            if not pk:
                return Response(error_result(code=1099, msg='pk参数错误！'))
            pk = int(pk)
            queryset_children = [pk]
            for i in range(4):
                queryset_father = queryset_children
                queryset_children = []
                for pk in queryset_father:
                    queryset = select_query(pk, i)
                    for item in queryset:
                        queryset_children.append(item.pk)
                if not queryset_children:
                    return Response(error_result(code=1099, msg='查询结果为空！'))
            knowledgepoint_queryset = []
            for pk in queryset_children:
                knowledgepoint_queryset.extend(select_query(pk, -1))
            total_len = len(knowledgepoint_queryset)
            serializer = KnowledgePointSimpleSerializer(knowledgepoint_queryset, many=True)
            return Response(
                error_result(code=200, msg=data_query_success_text, results=serializer.data, total=total_len))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


# 计算下载试题的费用,参数为只含题目id的一维数组，如果下载过的不计费
class PaymentAmountWithDownloadQuestionApiView(APIView):

    def get(self, request):
        try:
            question_list_downloading = request.query_params.get('questionList')
            question_id_list_charge = []
            if not question_list_downloading:
                return Response(error_result(code=1099, msg='参数错误！'))
            question_list_downloading = question_list_downloading.split(',')
            question_list_downloaded = DownloadQuestionLog.objects.filter(user__id=request.user.id)
            if not question_list_downloaded:
                question_id_list_charge = question_list_downloading
            else:
                question_list_downloaded = question_list_downloaded.values('id')
                question_id_list_charge = [i for i in question_list_downloading if i in question_list_downloaded]
            question_list_charge = Questions.objects.filter(pk__in=question_id_list_charge)
            total_cost = 0
            for item in question_list_charge:
                total_cost += item.bank_point
            return Response(error_result(code=200, msg=data_query_success_text, results=total_cost))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


# 计算下载导学案的费用,参数为导学案id，如果导学案下载过，不计费，返回url
class PaymentAmountWithDownloadPaperApiView(APIView):

    def post(self, request):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                total_fei = 0
                paper_id = request.data.get('paper_id')
                paper_server_obj = GuidePaper.objects.get(pk=paper_id)
                # 导学案不存在
                if not paper_server_obj:
                    return Response(error_result(code=1099, msg="错误", results='该导学案不存在'))
                paper_list_downloaded_history = DownloadGuidePaperLog.objects.filter(Q(user__id=request.user.id) &
                                                                                     Q(guide_paper__id=paper_id))
                # 该用户从未下载过导学案
                if not paper_list_downloaded_history:
                    # 该导学案不在历史下载记录中，则总费用是该导学案的库币，同时将该导学案写入下载记录（异步）、还有库币的变化
                    total_fei = paper_server_obj.bank_point
                    if request.user.bank_point < total_fei:
                        return Response(error_result(code=1099, msg="错误", results='您的库币余额不够'))
                    # 创建下载记录
                    create_download_guide_paper_log.delay(guide_paper=paper_id, user=request.user.id)
                    # print('本次学思案总费用是：', total_fei)
                    # 创建库币转账记录，并信号同步个人库币变化，收币方为公司，发币方为下载者
                    if total_fei > 0:
                        username = Company.objects.first().president
                        user_company = User.objects.get(username=username)
                        create_customer_bank_point_log_change.delay(operator=request.user.pk,
                                                                    receiver=user_company.pk,
                                                                    point_amount=total_fei,
                                                                    transaction_type=1,
                                                                    remarks='下载导学案')
                        create_customer_bank_point_log_change.delay(operator=user_company.pk,
                                                                    receiver=paper_server_obj.owner.pk,
                                                                    point_amount=total_fei * income_rate,
                                                                    transaction_type=1,
                                                                    remarks='被他人下载导学案')
                        create_send_notification.delay(user=request.user.pk,
                                                       content='你下载了导学案' + paper_server_obj.title + '共扣除库币'
                                                               + str(paper_server_obj.bank_point))
                file_url = settings.SERVER_IP + '/' + paper_server_obj.file

                print("导学案下载完毕")
                return Response(error_result(code=200, msg='下载成功！', results=file_url))
            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500, msg="错误", results='no'))

    def get(self, request):
        try:
            total_fei = 0
            paper_id = request.query_params.get('paper_id')
            paper_server_obj = GuidePaper.objects.get(pk=paper_id)
            # 导学案不存在
            if not paper_server_obj:
                return Response(error_result(code=1099, msg="错误", results='该导学案不存在'))
            paper_list_downloaded = DownloadGuidePaperLog.objects.filter(user__id=request.user.id)
            # 该用户从未下载过导学案，即历史下载记录不存在该导学案
            if not paper_list_downloaded:
                total_fei = paper_server_obj.bank_point
            # print("导学案费用", total_fei)
            return Response(error_result(code=200, msg='下载成功！', results=total_fei))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


# 计算下载试卷的的费用,参数为试卷id，如果该试卷下载过，不计费, 参数file_type的可选值为file 或file_explain
class PaymentAmountWithDownloadExaminationPaperApiView(APIView):

    def post(self, request):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                total_fei = 0
                paper_id = request.data.get('paper_id')
                file_type = request.data.get('file_type')
                paper_server_obj = ExaminationPaper.objects.get(pk=paper_id)
                # 试卷不存在
                if not paper_server_obj:
                    return Response(error_result(code=1099, msg="错误", results='该试卷不存在'))
                paper_list_downloaded_history = DownloadExaminationPaperLog.objects.filter(Q(user__id=request.user.id) &
                                                                                           Q(
                                                                                               examination_paper=paper_id))
                # 判断文件连接是否存在
                if file_type == 'file' and paper_server_obj.file == '':
                    return Response(error_result(code=1099, msg="错误", results='您请求的试卷文件不存在'))
                elif file_type == 'file_explain' and paper_server_obj.file_explain == '':
                    return Response(error_result(code=1099, msg="错误", results='您请求的试卷文件不存在'))
                # 该用户从未下载过导学案
                if not paper_list_downloaded_history:
                    # 该导学案不在历史下载记录中，则总费用是该导学案的库币，同时将该导学案写入下载记录（异步）、还有库币的变化
                    total_fei = paper_server_obj.bank_point
                    if request.user.bank_point < total_fei:
                        return Response(error_result(code=1099, msg="错误", results='您的库币余额不够'))
                    # 创建下载记录
                    create_download_examination_paper_log.delay(examination_paper=paper_id, user=request.user.id)
                    # print('本次学思案总费用是：', total_fei)
                    # 创建库币转账记录，并信号同步个人库币变化，收币方为公司，发币方为下载者
                    if total_fei > 0:
                        username = Company.objects.first().president
                        user_company = User.objects.get(username=username)
                        create_customer_bank_point_log_change.delay(operator=request.user.pk,
                                                                    receiver=user_company.pk,
                                                                    point_amount=total_fei,
                                                                    transaction_type=15,
                                                                    remarks='下载了试卷' + paper_server_obj.title)
                        create_customer_bank_point_log_change.delay(operator=user_company.pk,
                                                                    receiver=paper_server_obj.owner.pk,
                                                                    point_amount=round(total_fei * income_rate, 1),
                                                                    transaction_type=15,
                                                                    remarks='试卷' + paper_server_obj.title + '被下载')
                        create_send_notification.delay(user=request.user.pk,
                                                       content='你下载了试卷' + paper_server_obj.title + '共扣除库币'
                                                               + str(paper_server_obj.bank_point))
                file_url = settings.SERVER_IP + '/' + paper_server_obj.file
                # print("试卷下载完毕")
                return Response(error_result(code=200, msg='下载成功！', results=file_url))
            except Exception as E:
                print('Exception:', E)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500, msg="错误", results='no'))

    def get(self, request):
        try:
            total_fei = 0
            paper_id = request.query_params.get('paper_id')
            paper_server_obj = GuidePaper.objects.get(pk=paper_id)
            # 导学案不存在
            if not paper_server_obj:
                return Response(error_result(code=1099, msg="错误", results='该导学案不存在'))
            paper_list_downloaded = DownloadGuidePaperLog.objects.filter(user__id=request.user.id)
            # 该用户从未下载过导学案，即历史下载记录不存在该导学案
            if not paper_list_downloaded:
                total_fei = paper_server_obj.bank_point
            # print("导学案费用", total_fei)
            return Response(error_result(code=200, msg='下载成功！', results=total_fei))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg="错误", results='no'))


# 试卷作答
class AnswerPaperOfUserAPIView(APIView):

    def get(self, request):
        server_list = ChallengePaperOfUser.objects.filter(user__pk=request.user.id)
        if not server_list:
            return Response(error_result(code=1099))
        serializer = ChallengePaperOfUserModelSerializer(server_list, many=True)
        return Response(error_result(code=200, results=serializer.data))


class AnswerPaperOfUserModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = ChallengePaperOfUser
    serializer_class = ChallengePaperOfUserModelSerializer
    filter_fields = ('user',)
    pagination_class = SmallPage
    ordering_fields = ('-add_time',)

    # 查询自己的所有通知
    @action(methods=['get'],
            detail=False,
            url_path='only_owner',
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission])
    def only_owner(self, request):
        try:
            objs = self.get_queryset().objects.filter(user__pk=request.user.id)
            if not objs:
                return Response(error_result(code=1099))
            serializer = self.get_serializer(data=objs, many=True)
            if serializer.is_valid(raise_exception=False):
                return Response(error_result(code=200, results=serializer.data))
            else:
                return Response(error_result(code=1099, results=serializer.errors))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 用户提交试卷作答记录
    @action(methods=['post'],
            detail=False,
            url_path='create_log',
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def create_log(self, request, *args, **kwargs):
        try:
            # 如果存在本人有该试卷的提交记录，则不能提交
            paper_id = request.data['paper']
            server_obj = self.get_queryset().objects.filter(Q(user__pk=request.user.id) & Q(paper__pk=paper_id))
            # print("最近一次", server_obj)
            if server_obj:
                return Response(error_result(code=1022, results='已经提交过该试卷，不可以重复提交'))

            request.data['user'] = request.user.pk
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='提交失败，参数错误', msg=serializer.errors))
            serializer.save()
            question_list = server_obj.questions
            # 异步创建做题记录
            has_do_questions = UserDoQuestionLog.objects.filter(user__id=request.user.id).values('question')
            has_do_questions_list_id = convert_dic_list(has_do_questions, key='question')
            for item in question_list:
                if item.id not in has_do_questions_list_id:
                    create_user_do_question_log.delay(question=item.id, user=request.user.id)
            question_number = request.data['question_amount']
            # 每题0.5库币，跟题目的价值没关系
            challenge_costs = round(question_number // 2)
            paper_owner = ExaminationPaper.objects.get(pk=paper_id).owner
            # 获取公司账号
            username_company = Company.objects.first().president
            user_company = User.objects.get(username=username_company)
            create_customer_bank_point_log_change.delay(operator=request.user.id,
                                                        receiver=user_company.pk,
                                                        point_amount=challenge_costs,
                                                        transaction_type=11,
                                                        remarks='刷题')
            create_customer_bank_point_log_change.delay(operator=user_company.pk,
                                                        receiver=paper_owner.pk,
                                                        point_amount=challenge_costs * 0.7,
                                                        transaction_type=11,
                                                        remarks='被他人刷题')
            return Response(error_result(code=200, results='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 轮播图
class SwiperImageModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = SwiperImage
    serializer_class = SwiperImageModelSerializer
    filter_fields = ('type',)
    pagination_class = SmallPage
    ordering_fields = ('top', '-add_time',)
    authentication_classes = []
    permission_classes = []

    # 根据类型查看
    @action(methods=['get'],
            detail=False,
            url_path='get_swiper_image',
            authentication_classes=[],
            permission_classes=[])
    def get_swiper_image(self, request):
        try:
            objs = self.get_queryset().objects.all().values("image_url", "href_url", 'title')
            if not objs:
                return Response(error_result(code=1099))
            for i in range(len(objs)):
                if objs[i]['image_url'] != '':
                    objs[i]['image_url'] = settings.SERVER_IP + '/static/uploads/' + objs[i]['image_url']
            return Response(error_result(code=200, results=objs))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 公告
class BulletinModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Bulletin
    serializer_class = BulletinModelSerializer
    filter_fields = ('title',)
    pagination_class = SmallPage
    ordering_fields = ('-add_time',)
    authentication_classes = []
    permission_classes = []

    # 根据类型查看
    @action(methods=['get'],
            detail=False,
            url_path='get_bulletin',
            authentication_classes=[],
            permission_classes=[])
    def get_bulletin(self, request):
        try:
            obj_list = cache.get('get_bulletin')
            if obj_list:
                print('从缓存直接返回通知栏')
                return Response(error_result(code=200, results=obj_list))
            # 直接用values序列化
            objs = self.get_queryset().objects.all().values('title', 'content', 'add_time')
            if not objs:
                return Response(error_result(code=1099))
            cache.set('get_bulletin', objs, 30 * 24 * 60 * 60)
            return Response(error_result(code=200, results=objs))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 分类图标
class CategoryModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Category
    serializer_class = CategoryModelSerializer
    filter_fields = ('name',)
    pagination_class = SmallPage
    ordering_fields = ('name',)
    authentication_classes = []
    permission_classes = []

    # 根据类型查看
    @action(methods=['get'],
            detail=False,
            url_path='get_category',
            authentication_classes=[],
            permission_classes=[])
    def get_category(self, request):
        try:
            objs = self.get_queryset().objects.all().values("name", 'icon', 'href_url')
            if not objs:
                return Response(error_result(code=1099))
            for i in range(len(objs)):
                if objs[i]['icon'] != '':
                    objs[i]['icon'] = settings.SERVER_IP + '/static/uploads/' + objs[i]['icon']
            return Response(error_result(code=200, results=objs))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 教辅甄选
class BookModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Book
    serializer_class = BookModelSerializer
    filter_fields = ('name',)
    pagination_class = SmallPage
    ordering_fields = ('name',)

    # 根据类型查看
    @action(methods=['get'],
            detail=False,
            url_path='get_book',
            authentication_classes=[],
            permission_classes=[])
    def get_book(self, request):
        try:
            objs = self.get_queryset().objects.all().values('id', "name", 'cover', 'file', 'author', 'publish_date',
                                                            'remark')
            if not objs:
                return Response(error_result(code=1099))
            return Response(error_result(code=200, results=objs))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 允许录入试题的申请
class PermitExplainPaperOfUserModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = PermitExplainPaperOfUser
    serializer_class = PermitExplainPaperOfUserModelSerializer
    filter_fields = ('add_time',)
    pagination_class = SmallPage
    ordering_fields = ('add_time',)

    # 用户提交某试卷允许录入申请
    @action(methods=['post'],
            detail=False,
            url_path='create_permit',
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def create_permit(self, request, *args, **kwargs):
        try:
            # 如果存在本人有该试卷的提交记录，则不能提交
            paper_id = request.data['paper']
            server_obj = self.get_queryset().objects.filter(Q(user__pk=request.user.id) & Q(paper__pk=paper_id))
            if server_obj:
                return Response(error_result(code=1022, results='已经提交过该试卷的权限申请', msg='已经申请过了'))
            request.data['user'] = request.user.pk
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='提交失败，参数错误', msg=serializer.errors))
            serializer.save()
            return Response(error_result(code=200, results='ok', msg='提交成功'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 已做题目多对多
class UserDoQuestionLogModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = UserDoQuestionLog
    serializer_class = UserDoQuestionLogModelSerializer
    filter_fields = ('add_time',)
    pagination_class = SmallPage
    ordering_fields = ('add_time',)

    # 筛选提交者已做的题目
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_question_has_done_owner(self, request):
        """筛选指定用户id为owner下已做的题目"""
        current_user_server = User.objects.get(id=request.user.id)
        question_list_server = self.get_queryset().objects.filter(user__id=request.user.id)
        # print('question_list:', question_list_server)
        if not question_list_server:
            return Response(error_result(code=1099, results=[], msg=''))
        has_do_question_list_id = convert_dic_list(dict_list=question_list_server.values('question'), key='question')
        # serializer = self.get_serializer(question_list_server, many=True)
        return Response(error_result(code=200, results=has_do_question_list_id, total=len(question_list_server)))

    # 用户提交题目已读记录，同时扣除库币
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_question_has_done_owner(self, request, *args, **kwargs):
        try:
            # 如果存在本人有该试卷的提交记录，则不能提交
            question_id = request.data['question']
            question_obj_server = Questions.objects.get(id=question_id)
            if question_obj_server is None:
                return Response(error_result(code=1099, results=[], msg='question_id错误'))
            question_owner = question_obj_server.owner
            if question_owner.id == request.user.id:
                return Response(error_result(code=200, results=[], msg='自己的试题，不扣钱'))
            user_do_question_obj_server = UserDoQuestionLog.objects.filter(Q(question__id=question_id) &
                                                                           Q(user__id=request.user.id))
            if user_do_question_obj_server:
                return Response(error_result(code=1099, results=[], msg='该题目已经读过，没必要创建已读记录'))
            request.data['user'] = request.user.id
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='提交失败，参数错误', msg=serializer.errors))
            serializer.save()
            print('create has done recodes ok!')
            # 获取公司账号
            username_company = Company.objects.first().president
            user_company = User.objects.get(username=username_company)
            # 创建扣钱记录
            create_customer_bank_point_log_change.delay(operator=request.user.pk,
                                                        receiver=user_company.pk,
                                                        point_amount=price_do_exercise_per_question,
                                                        transaction_type=11,
                                                        remarks='做练习题目')
            create_customer_bank_point_log_change.delay(operator=user_company.pk,
                                                        receiver=question_owner.id,
                                                        point_amount=round(price_do_exercise_per_question * income_rate,
                                                                           1),
                                                        transaction_type=11,
                                                        remarks='试题被他人刷题')
            print('pay for fee')
            has_do_question_list_server_new = self.get_queryset().objects.filter(user__id=request.user.id).values(
                'question')
            has_do_question_list_id = convert_dic_list(dict_list=has_do_question_list_server_new, key='question')
            return Response(error_result(code=200, results=has_do_question_list_id, msg='创建题目已读记录提交成功'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 试题的微视频
class VideoModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Video
    serializer_class = VideoModelSerializer
    filter_fields = ('owner', 'add_time',)
    pagination_class = SmallPage
    ordering_fields = ('-add_time', 'owner',)

    # 用于千牛网视频回调。提交题目和视频url，创建一对一,需要提供参数question试题id，title,url,其中url必须唯一不重复
    # 并把url发给草料二维码生成二维码图片，保存到字段qrcode中
    @action(methods=['post', 'get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[]
            )
    def create_question_video(self, request, *args, **kwargs):
        try:
            if request.method == 'POST':
                question = request.data.get('question')
                url = request.data.get('key')
                owner = request.data.get('owner')
            elif request.method == 'GET':
                question = request.query_params.get('question')
                url = request.query_params.get('key')
                owner = request.query_params.get('owner')
            response_body = {
                'key': url,
                # 可以包含其他你想要返回的信息
                'code': 200
            }
            question = int(question)
            video_exist_server = Video.objects.filter(question__id=question)
            print('video_exist-server:', video_exist_server)
            # 如果该试题已经存在视频，则更新该试题的视频，否则新建试题的视频记录
            is_update = False
            if len(video_exist_server) > 0:
                print('error! this question has video already')
                is_update = True
            # 把视频网址发给草料服务器接口，获取二维码图片，并存储到数据表内, 二维码名称与视频名称一样
            qrcode_name = url.split('.')[0] + '.png'
            file_path = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.QRCODE_ROOT + qrcode_name
            file, boolean_result = get_qrcode_and_store(url, file_path)
            if not boolean_result:
                print('caoliao qrcode fail!')
                return Response(error_result(code=202, results=response_body, msg='QRcode store error!'))
            question_obj = Questions.objects.get(id=question)
            owner_obj = User.objects.get(id=owner)
            new_data = {'question': question_obj, 'owner': owner_obj, 'url': url, 'qr_code': file_path}
            print('new_data:', new_data)
            if not is_update:
                print('create!')
                serializer = self.get_serializer(data=new_data)
            else:
                print('update!')
                video_exist_server = video_exist_server.first()
                serializer = self.get_serializer(instance=video_exist_server, data=new_data, partial=True)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
            if not is_update:
                serializer.create(new_data)
            else:
                serializer.update(video_exist_server, new_data)
            # serializer.save()
            print('保存视频成功！')
            return Response(response_body, status=200)
        except Exception as e:
            print('Exception:', e)

    # 保存用户上传的视频文件，并创建
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def upload_creat_question_video(self, request, *args, **kwargs):
        try:
            # 如果标题未提供，则改为当前时间
            question = request.data.get('question')
            new_data = {'question': question}
            title = request.data.get('title')
            if title is not None and title == '':
                title = str(datetime.now())
            new_data['title'] = title
            new_data['owner'] = request.user.id
            url = request.data.get('url')
            if (url is not None and url == '') or url is None:
                received_file = request.FILES.get("upload_file")
                file_prefix = received_file.name.split(".")[-1]
                if file_prefix.upper() not in ['MP4']:
                    return Response(error_result(code=1099, msg='文件类型错误'))
                new_name = get_new_name() + '.' + file_prefix
                file_path = os.path.join(settings.VIDEO_ROOT, new_name)
                ff = open(file_path, 'wb')
                for chunk in received_file.chunks():
                    ff.write(chunk)
                ff.close()
                new_data['url'] = settings.VIDEO_URL + new_name
            elif url is not None and url != '':
                new_data['url'] = url

            serializer = self.get_serializer(data=new_data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='no', msg=serializer.errors))
            serializer.save()
            return Response(error_result(code=200, results='ok', msg='微视频提交成功'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选提交者已经提交到视频题目id列表
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_video_question_list_owner(self, request):
        try:
            question_list_server = self.get_queryset().objects.filter(owner__id=request.user.id)
            # print('question_list:', question_list_server)
            if not question_list_server:
                return Response(error_result(code=1099, results=[], msg=''))
            has_do_question_list_id = convert_dic_list(dict_list=question_list_server.values('question'),
                                                       key='question')
            # serializer = self.get_serializer(question_list_server, many=True)
            return Response(error_result(code=200, results=has_do_question_list_id, total=len(question_list_server)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选本人的视频信息
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_video_list_owner(self, request):
        try:
            video_list_server = self.get_queryset().objects.filter(owner__id=request.user.id)
            # print('question_list:', question_list_server)
            if not video_list_server:
                return Response(error_result(code=1099, results=[], msg=''))
            page = self.paginate_queryset(video_list_server)
            serializer = self.get_serializer(page, many=True)
            return Response(
                error_result(code=200, results=serializer.data, total=len(video_list_server)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 提供七牛云的token
class QINIUTokenAPIView(APIView):

    def post(self, request):
        try:
            suffix = '.mp4'
            key = request.data.get('key')
            print('qiniu token key:', key)
            question = request.data.get('question')
            if key is not None:
                suffix_index = key.rfind('.')
                if suffix_index != -1:
                    suffix = key[suffix_index + 1:]
                token = get_token(url=key, suffix=suffix, question=question)
            else:
                token = get_token()
            if not key:
                return Response(error_result(code=1099, results={}, msg=''))
            return Response(error_result(code=200, msg="获取七牛云token成功", results=token))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500))


class QrCodeVerifyAPIView(APIView):
    permission_classes = []
    authentication_classes = []

    def get(self, request):
        try:
            return HttpResponse("52581db3a8597c63f40ced91ba391cf6")
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 已观看的视频多对多
class UserPlayVideoLogModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = UserPlayVideoLog
    serializer_class = UserPlayVideoLogModelSerializer
    filter_fields = ('add_time',)
    pagination_class = SmallPage
    ordering_fields = ('add_time',)

    # 筛选提交者已观看的视频
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_video_has_played_owner(self, request):
        """筛选指定用户id下已观看的视频列表"""
        server_list = self.get_queryset().objects.filter(user__id=request.user.id)
        # print('question_list:', question_list_server)
        if not server_list:
            return Response(error_result(code=1099, results=[], msg=''))
        has_played_video_list_id = convert_dic_list(dict_list=server_list.values('video'), key='video')
        return Response(error_result(code=200, results=has_played_video_list_id, total=len(server_list)))

    # 用户提交已看视频记录，同时扣除库币（若该视频是历史已看视频，则不扣费）
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_video_has_played_log(self, request, *args, **kwargs):
        try:
            # 如果存在提交者的历史记录，则不能提交
            video_id = request.data['video']
            obj_server = Video.objects.get(id=video_id)
            if obj_server is None:
                return Response(error_result(code=1099, results=[], msg='video的id错误'))
            video_owner = obj_server.owner
            if video_owner == request.user.id:
                return Response(error_result(code=200, results=[], msg='自己的视频，不扣钱'))
            obj_log_server = self.get_queryset().objects.filter(Q(video__id=video_id) &
                                                                Q(user__id=request.user.id))
            if obj_log_server:
                return Response(error_result(code=202, results=[], msg='该视频已经播放过，没必要再创建记录'))
            new_data = {'user': request.user.id, 'video': video_id}
            serializer = self.get_serializer(data=new_data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='提交失败，参数错误', msg=serializer.errors))
            serializer.save()
            # 获取公司账号
            username_company = Company.objects.first().president
            user_company = User.objects.get(username=username_company)
            # 创建扣钱记录
            create_customer_bank_point_log_change.delay(operator=request.user.pk,
                                                        receiver=user_company.pk,
                                                        point_amount=price_play_video,
                                                        transaction_type=14,
                                                        remarks='观看微视频')
            create_customer_bank_point_log_change.delay(operator=user_company.pk,
                                                        receiver=video_owner,
                                                        point_amount=round(price_play_video * income_rate, 1),
                                                        transaction_type=14,
                                                        remarks='被他人观看微视频')
            return Response(error_result(code=200, results='ok', msg='创建已看视频记录成功'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class TagModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Tag
    serializer_class = TagSerializer
    filter_fields = ('name',)
    pagination_class = BigPage
    ordering_fields = ('owner',)

    # 筛选自己名下的tag
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_all_tag(self, request):
        """得到所有自己名下的tag"""
        server_list = self.get_queryset().objects.filter(owner__id=request.user.id)
        # print('question_list:', question_list_server)
        if not server_list:
            return Response(error_result(code=1099, results=[], msg='没有标签'))
        # page = self.paginate_queryset(server_list)
        serializer = self.get_serializer(server_list, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(server_list)))

    # 筛选自己名下的tag
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_question(self, request):
        """得到某个tag下 question"""
        try:
            tag_id = request.query_params.get('tag_id')
            server_list = self.get_queryset().objects.filter(pk=tag_id)
            if not server_list:
                return Response(error_result(code=1099, results=[], msg=''))
            server_obj = server_list.first()
            question = server_obj.question_set.filter(online=True)
            serializer = QuestionPrivateSerializer(question, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(serializer.data)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 新增tag
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_tag(self, request, *args, **kwargs):
        try:
            owner_id = self.request.user
            tag_name = request.data['tag_name']
            tag_obj = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(name=tag_name))
            if not tag_obj:
                return Response(error_result(code=109, msg='标签名已存在', results="no"))
            request.data['owner'] = owner_id
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid():
                print("序列化错误:", serializer.errors)
                return Response(error_result(code=406, msg=serializer.errors, results="data_error_text"))
            serializer.save()
            data = {'count': 1, 'results': 'ok', 'msg': '创建标签成功'}
            return Response(error_result(code=200, msg="恭喜，添加成功！", results=data))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 修改单个标签名称
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def modify_name(self, request, *args, **kwargs):
        try:
            tag_id = request.data['tag_id']
            name = request.data['name']
            obj_server = self.get_queryset().objects.filter(pk=tag_id)
            if not obj_server:
                return Response(error_result(code=1099, msg='申请修改的标签id错误', results='no'))
            if name is None or name == '':
                return Response(error_result(code=1099, msg='申请修改的节点名不能空', results='no'))
            modify_dic = {'name': name}
            obj_server = obj_server.first()
            serializer = self.get_serializer(instance=obj_server, data=modify_dic, partial=True)
            if not serializer.is_valid():
                return Response(error_result(code=1099, msg=data_error_text, results=serializer.errors))
            serializer.save()
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


def nest_node_question(node_list: list):
    question_list = []
    for node_obj in node_list:
        question = node_obj.question.filter(online=True)
        if len(question) > 0:
            question_list.append(question)
        children = node_obj.children.all()
        if children:
            temp_list = nest_node_question(children)
            if len(temp_list) > 0:
                question_list += temp_list
        else:
            return question_list
    return question_list


class QuestionPrivateModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Question
    serializer_class = QuestionPrivateSerializer
    filter_fields = ('content',)
    pagination_class = SmallPage
    ordering_fields = ('order',)

    def update(self, request, *args, **kwargs):
        """
        接受知识点为字典单个对象或者列表（内含字典）的多个对象的修改
        如果是单个字典对象，先转换成单个对象的列表，最后统一成提交列表
        """
        try:
            request_data = request.data
            pk = kwargs.get('pk', None)
            modify_data_list = []
            question_list = []
            if pk and isinstance(request_data, dict):
                question_id = request_data.pop("id", None)
                node = request_data.pop("node", None)
                pk = int(pk)
                if pk == question_id:
                    request_data['last_edit_time'] = datetime.now()
                    request_data['online'] = False
                    question_server = Question.objects.get(pk=pk)
                    if node:
                        node_list_id = convert_dic_list(dict_list=node)
                        node_list_server = Node.objects.filter(id__in=node_list_id)
                        question_server.node_set.set(list(node_list_server))
                    modify_data_list.append(request_data)
                    question_list.append(question_server)
                else:
                    return Response(error_result(code=406, msg="id不一致", results="data_error_text"))
            elif not pk and isinstance(request_data, list):
                for item in request_data:
                    pk = item.pop("id")
                    item['last_edit_time'] = datetime.now()
                    item['online'] = False
                    question_obj = Question.objects.get(pk=pk)
                    question_list.append(question_obj)
                    modify_data_list.append(item)
            else:
                return Response(error_result(code=406, msg="data_error_text", results="data_error_text"))
            question_ser = QuestionPrivateSerializer(instance=question_list, data=modify_data_list, many=True,
                                                     partial=True)
            if not question_ser.is_valid(raise_exception=False):
                return Response(error_result(code=406, msg=question_ser.errors, results=question_ser.errors))
            else:
                question_ser.save()
                data = {'count': len(question_ser.data), 'results': question_ser.data}
                return Response(error_result(code=200, msg=data_success_text, results=data))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选自己名下的所有试题
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_myself_question(self, request):
        """得到所有自己名下的question"""
        server_list = self.get_queryset().objects.filter(Q(owner__id=request.user.id))
        if not server_list:
            return Response(error_result(code=1099, results=[], msg=''))
        page = self.paginate_queryset(server_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(server_list)))

    # 筛选自己名下的公开试题
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_all_default(self, request):
        """得到所有自己名下的question"""
        server_list = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(online=True))
        if not server_list:
            return Response(error_result(code=1099, results=[], msg=''))
        page = self.paginate_queryset(server_list)
        serializer = self.get_serializer(page, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(server_list)))

    # 筛选自己名下的tag
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_tag(self, request):
        """得到某个试题的tag"""
        question_id = request.query_params.get('id')
        server_list = self.get_queryset().objects.get(pk=question_id)
        if not server_list:
            return Response(error_result(code=1099, results=[], msg=''))
        tag = server_list.tag
        serializer = TagSerializer(tag, many=True)
        return Response(error_result(code=200, results=serializer.data, total=len(server_list)))

    # 新增私有试题
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_question(self, request, *args, **kwargs):
        try:
            question_modify_list = []
            question = request.data["questionList"]
            node_question_list = []
            node_list = []
            question_application_list = []
            if isinstance(question, dict):
                request.data['owner'] = request.user.id
                request.data['online'] = False
                question_modify_list.append(question)
                for node in question['node']:
                    node_question_list.append({'node': node['id'], 'question': 0})
                    node_list.append(node['id'])
            elif isinstance(question, list):
                for item in question:
                    item['owner'] = request.user.id
                    item['online'] = False
                    for node in item['node']:
                        node_question_list.append({'node': node['id'], 'question': 0})
                        if node['id'] not in node_list:
                            node_list.append(node['id'])
                    # item['node_set'] = node_list
                    del item['node']
                    # 修改临时图片路径为正式图片路径
                    if '/temp/' in item['content'] and 'src' in item['content']:
                        image_list = extract_image_paths(item['content'])
                        for image in image_list:
                            destination_path = os.path.join(settings.BASE_DIR, settings.IMAGE_URL).replace('\\', '/')
                            copy_and_delete(image, destination_path)
                        item['content'] = item['content'].replace('temp/', 'images/')
                    if '/temp/' in item['answer']:
                        image_list = extract_image_paths(item['answer'])
                        for image in image_list:
                            destination_path = os.path.join(settings.BASE_DIR, settings.IMAGE_URL).replace('\\', '/')
                            copy_and_delete(image, destination_path)
                        item['answer'] = item['answer'].replace('temp/', 'images/')
                    if '/temp/' in item['explain']:
                        image_list = extract_image_paths(item['explain'])
                        for image in image_list:
                            destination_path = os.path.join(settings.BASE_DIR, settings.IMAGE_URL).replace('\\', '/')
                            copy_and_delete(image, destination_path)
                        item['explain'] = item['explain'].replace('temp/', 'images/')
                    question_modify_list.append(item)
            question_ser = self.get_serializer(data=question_modify_list, many=True)
            if not question_ser.is_valid():
                print('试题序列化错误:', question_ser.errors)
                return Response(error_result(code=406, msg=question_ser.errors, results=data_error_text))
            question_ser.save()
            # 单独添加所有的node-question多对多关系记录
            for i in range(len(node_question_list)):
                node_question_list[i]['question'] = question_ser.instance[i].id
            node_question_ser = Node2QuestionSerializer(data=node_question_list, many=True)
            if not node_question_ser.is_valid():
                print('试题序列化错误')
                return Response(error_result(code=406, msg=node_question_ser.errors, results="data_error_text"))
            node_question_ser.save()
            # 以下添加试题上线审核QuestionPrivateOnlineApplication
            for question in question_ser.instance:
                question_application_list.append({'user': request.user.id, 'question': question.id})
            question_application_ser = QuestionPrivateOnlineApplicationModelSerializer(
                data=question_application_list, many=True)
            if not question_application_ser.is_valid():
                print('试题序列化错误')
                return Response(error_result(code=406, msg=question_application_ser.errors, results="data_error_text"))
            question_application_ser.save()
            return Response(error_result(code=200, msg="恭喜，添加成功！", results='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 新增私有试题的标签,如果标签名已经存在，则添加到该便签下，如果标签名不存在，则新建。
    # 如果已经存在该标签和试题的关联，则返回提示，该关联没有必要
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def add_tag(self, request, *args, **kwargs):
        try:
            question_id = request.data["question_id"]
            question_obj = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=question_id))
            if not question_obj:
                return Response(error_result(code=406, msg='他人试题，无权关联', results=data_error_text))
            question_obj = question_obj.first()
            tag_name = request.data['tag_name']
            tag_obj = Tag.objects.filter(Q(owner__id=request.user.id) & Q(name=tag_name))
            if tag_obj:
                tag_obj_list = question_obj.tag.all()
                tag_name_list = convert_dic_list(dict_list=tag_obj_list.values('name'), key='name')
                if tag_name in tag_name_list:
                    return Response(error_result(code=1098, msg='该标签已经存在，没必要', results=data_error_text))
                tag_obj = tag_obj.first()
                question_obj.tag.add(tag_obj)
            else:
                tag_new = {'name': tag_name, 'owner': request.user.id}
                tag_ser = TagSerializer(data=tag_new)
                if not tag_ser.is_valid():
                    print('tag serializer error:', tag_ser.errors)
                    return Response(
                        error_result(code=1099, msg=tag_ser.errors, results="data_error_text"))
                tag_ser.save()
                tag_question_new = {'question': question_id, 'tag': tag_ser.instance.id}
                tag_question_ser = Tag2QuestionModelSerializer(data=tag_question_new)
                if not tag_question_ser.is_valid():
                    print('tag serializer error:', tag_question_ser.errors)
                    return Response(error_result(code=1099, msg=tag_question_ser.errors, results=data_error_text))
                tag_question_ser.save()
            return Response(error_result(code=200, msg="恭喜，添加成功！", results='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 删除试题和标签的关联
    @action(methods=['delete'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def delete_tag(self, request, *args, **kwargs):
        try:
            question_id = request.query_params['question_id']
            question_obj = self.get_queryset().objects.filter(Q(id=question_id) & Q(owner__id=request.user.id))
            if not question_obj:
                return Response(error_result(code=406, msg='他人试题，你无权修改', results=data_error_text))
            question_obj = question_obj.first()
            tag_id = request.query_params['tag_id']
            tag_obj = Tag.objects.get(id=tag_id)
            question_obj.tag.remove(tag_obj)
            return Response(error_result(code=200, msg="恭喜，删除成功！", results='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选知识点下的题目列表，含该节点下的嵌套孩子的试题
    @action(methods=['get'], detail=False)
    def get_question_id_type(self, request):
        """筛选某知识点下的题目"""
        try:
            node_id = request.query_params['id']
            server_obj = Node.objects.filter(id=node_id).first()
            if not server_obj:
                return Response(error_result(code=1099, results='no', msg='node id错误'))
            children = server_obj.children.all()
            # 末端节点，直接返回它的所有试题，非末端节点，则嵌套递归获取它名下的所有试题
            question_list = []
            if not children:
                question_list += server_obj.question.filter(online=True)
            else:
                question_list += nest_node_question(children)
            if len(question_list) == 0:
                return Response(error_result(code=1099, results='no', msg='该节点下无试题'))
            page = self.paginate_queryset(question_list)
            serializer = self.get_serializer(page, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(question_list)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    @action(methods=['get'],
            detail=False,
            authentication_classes=[],
            permission_classes=[])
    def search_content_by_keyword(self, request):
        """题干的关键词搜索试题"""
        try:
            keyword = request.query_params['keyword']
            print('keyword:', keyword)
            if len(keyword) == 0:
                return Response(error_result(code=1099, results='no', msg='关键词不能空'))
            # question_list = self.get_queryset().objects.filter(Q(online=True) & Q(content__contains=keyword) &
            #                                                    Q(owner__id=request.user.id))
            question_list = self.get_queryset().objects.filter(Q(content__contains=keyword))
            if len(question_list) == 0:
                return Response(error_result(code=1099, results='no', msg='查询为空'))
            # page = BigPage().paginate_queryset(question_list, request)
            serializer = self.get_serializer(question_list, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(question_list)))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=500, msg='获取默认试题失败', results=[], total=0))


def nest_node(subject_list):
    node_list = []
    for i in range(len(subject_list)):
        node_obj = subject_list[i]
        node_obj_server = Node.objects.get(id=node_obj.id)
        node_item = {'id': node_obj.id, 'name': node_obj.name, 'order': node_obj.order, 'children': [],
                     'question_count': get_children_question_count(node_obj_server)}
        if i == 0:
            node_item['previous'] = 0
        else:
            node_obj_pre = subject_list[i - 1]
            node_item['previous'] = node_obj_pre.id
        children = Node.objects.filter(parent__id=node_obj.id)
        if children:
            node_item['children'] = nest_node(children)
            if len(node_item['children']) == 0:
                del node_item['children']
            node_list.append(node_item)
        else:
            if len(node_item['children']) == 0:
                del node_item['children']
            node_list.append(node_item)
    return node_list


# 递归方法获取该节点下的试题数量
def get_children_question_count(node: Node):
    s = 0
    children = Node.objects.filter(parent__id=node.id)
    if children:
        for child in children:
            s += get_children_question_count(child)
    else:
        return node.question.count()
    return s


# 获取所有末端节点
def get_end_node_nest(node: Node):
    node_list = []
    children = Node.objects.filter(parent__id=node.id)
    if children:
        temp_list = []
        for child in children:
            temp_list += get_end_node_nest(child)
        node_list += temp_list
    else:
        return [{'name': node.name, 'id': node.id}]
    return node_list


class NodeModelViewSet(CustomNoQuestionsModelViewSet):
    queryset = Node
    serializer_class = NodeSerializer
    filter_fields = ('content',)
    pagination_class = BigPage
    ordering_fields = ('order',)

    # 筛选自己名下和协作者名下的的node, 嵌套数组
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_all_node(self, request):
        """
        如果没有参数，则得到所有自己名下的嵌套node
        否则筛选出对应的course_id开始的所有试题
        """
        try:
            course_id = request.query_params.get('course_id')
            # 协助过的node列表
            teamwork = NodeCooperation.objects.filter(user__id=request.user.id)
            teamwork_node_ids = []
            if teamwork:
                teamwork_node_ids = convert_dic_list(dict_list=teamwork.values('node'), key='node')
            if course_id is None:
                course_list = self.get_queryset().objects.filter(
                    Q(owner__id=request.user.id) | Q(id__in=teamwork_node_ids))
            else:
                course_list = self.get_queryset().objects.filter(
                    (Q(owner__id=request.user.id) | Q(id__in=teamwork_node_ids)) & Q(pk=course_id))
            if not course_list:
                return Response(error_result(code=1099, results=[], msg=''))
            # server_list = subject_list.values('id', 'name', 'order')
            course_obj = course_list.first()
            children = Node.objects.filter(parent__id=course_obj.id)
            question_count = get_children_question_count(course_obj)
            node_list = [{'id': course_obj.id, 'name': course_obj.name, 'order': course_obj.name,
                          'question_count': question_count, 'previous': 0}]
            temp_list = nest_node(children)
            if len(temp_list) > 0:
                node_list += temp_list
            if not node_list:
                return Response(error_result(code=1099, results=[], msg=''))
            return Response(error_result(code=200, results=node_list, total=len(node_list)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 取消课程协作
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            permission_classes=[IsOwnerPermission]
            )
    def cancel_cooperation(self, request, *args, **kwargs):
        try:
            user_id = request.data['user_id']
            node_id = request.data['node_id']
            node_server = self.get_queryset().objects.filter(Q(pk=node_id) & Q(owner__id=request.user.id))
            if not node_server:
                return Response(error_result(code=1098, msg='你无权操作该课程', results='no'))
            obj_server = NodeCooperation.objects.get(Q(node__id=node_id) & Q(user__id=user_id))
            if not obj_server:
                return Response(error_result(code=1099, msg='申请删除的节点id错误', results='no'))
            obj_server.delete()
            return Response(error_result(code=200, msg='取消协作成功', results='OK'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='取消错题失败', results="", total=0))

    # 筛选自己名下的node的协作者
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_all_teamworker(self, request):
        """
        参数为node的id, 若没有node_id表示查询该用户所有的node
        """
        try:
            node_id = request.query_params.get('node_id')
            if node_id is None:
                node_list = self.get_queryset().objects.filter(owner__id=request.user.id)
            else:
                node_list = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=node_id))
            if not node_list:
                return Response(error_result(code=1099, results=[], msg=''))
            # server_list = subject_list.values('id', 'name', 'order')
            node_obj = node_list.first()
            teamworker = node_obj.teamworker.all()
            if not teamworker:
                return Response(error_result(code=1099, results=[], msg=''))
            serializer = UserSimpleModelSerializer(teamworker, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(teamworker)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选自己名下或者协作的他人的课程名,即没有parent的节点
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_all_course(self, request):
        """得到所有自己名下的顶端的course"""
        try:
            cooperate_node = NodeCooperation.objects.filter(user__id=request.user.id)
            if cooperate_node:
                cooperate_node_ids = cooperate_node.values('node_id')
                cooperate_node_ids_list = convert_dic_list(dict_list=cooperate_node_ids, key='node_id')
                subject_list = self.get_queryset().objects.filter(
                    (Q(owner__id=request.user.id) | Q(id__in=cooperate_node_ids_list)) & Q(parent__id=None))
            else:
                subject_list = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(parent__id=None))
            if not subject_list:
                return Response(error_result(code=1099, results=[], msg=''))
            serializer = self.get_serializer(subject_list, many=True)
            return Response(error_result(code=200, results=serializer.data, total=len(serializer.data)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 筛选自己名下或者协作的他人的末端节点，即没有children的节点
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_all_end_node(self, request):
        """得到所有自己名下的顶端node下 末尾节点"""
        try:
            # 先验证该用户是否有权限即是否为协作者或者拥有者
            has_permit = False
            top_node_id = request.query_params.get('top_node_id')
            cooperate_node = NodeCooperation.objects.filter(user__id=request.user.id)
            if cooperate_node:
                cooperate_node_ids = cooperate_node.values('node_id')
                cooperate_node_ids_list = convert_dic_list(dict_list=cooperate_node_ids, key='node_id')
                if int(top_node_id) in cooperate_node_ids_list:
                    has_permit = True

            owner_node = self.get_queryset().objects.filter(owner__id=request.user.id)
            if owner_node:
                owner_node_ids = owner_node.values('id')
                owner_node_ids_list = convert_dic_list(dict_list=owner_node_ids, key='id')
                if int(top_node_id) in owner_node_ids_list:
                    has_permit = True
            if not has_permit:
                return Response(error_result(code=1098, results=[], msg='没有权限'))
            top_node_obj = self.get_queryset().objects.get(id=top_node_id)
            end_node_list = get_end_node_nest(top_node_obj)
            # serializer = self.get_serializer(end_node_list, many=True)
            return Response(error_result(code=200, results=end_node_list, total=len(end_node_list)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 新增节点
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_node(self, request, *args, **kwargs):
        try:
            # 提供父节点id和本节点的名称,order是由父节点下的孩子数量+1决定
            name = request.data.get('name')
            parent_id = request.data.get('parent_id')
            order = 1
            if name is None or name == '':
                return Response(error_result(code=1099, results='no', msg='名称不能空'))
            # 如果有父节点
            if parent_id is not None:
                # 如果父亲节点是提交者拥有则不能建立子节点
                is_owner_server = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=parent_id))
                if not is_owner_server:
                    return Response(error_result(code=1095, results='no', msg='你不是作者，不能创建'))
                # 计算order
                parent_node_obj = self.get_queryset().objects.get(id=parent_id)
                if not parent_node_obj:
                    return Response(error_result(code=1099, results='no', msg='父节点不存在'))
                new_data = {'name': name, 'owner': request.user.id, 'parent': parent_id, 'cover': ''}
                children = parent_node_obj.children
                order = children.count() + 1
            # 如果没有父节点，即新建一个课程
            else:
                # 新建课程，先判断师父到达极限
                node_server = self.get_queryset().objects.filter(owner__id=request.user.id)
                if node_server:
                    myVIP = User.objects.get(id=request.user.id).VIP
                    can_create_count = myVIP * 5 + 1
                    if len(node_server) + 1 > can_create_count:
                        return Response(error_result(code=1095, results='no', msg='新建课程能力已经达到极限，请升级VIP'))
                new_data = {'name': name, 'owner': request.user.id}
            new_data['order'] = order
            serializer = self.get_serializer(data=new_data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='no', msg=serializer.errors))
            serializer.save()
            return Response(error_result(code=200, results='ok', msg='节点新增成功'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 修改单个节点名称
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def modify_name(self, request, *args, **kwargs):
        try:
            node_id = request.data['node_id']
            name = request.data['name']
            obj_server = self.get_queryset().objects.filter(pk=node_id)
            if not obj_server:
                return Response(error_result(code=1099, msg='申请修改的节点id错误', results='no'))
            if name is None or name == '':
                return Response(error_result(code=1099, msg='申请修改的节点名不能空', results='no'))
            is_owner_server = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=node_id))
            if not is_owner_server:
                return Response(error_result(code=1095, results='no', msg='你不是作者，不能创建'))
            modify_dic = {'name': name}
            obj_server = obj_server.first()
            serializer = self.get_serializer(instance=obj_server, data=modify_dic, partial=True)
            if not serializer.is_valid():
                return Response(error_result(code=1099, msg=data_error_text, results=serializer.errors))
            serializer.save()
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 删除单个节点名称
    @action(methods=['delete'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def delete_node(self, request, *args, **kwargs):
        try:
            node_id = request.query_params.get('node_id')
            obj_server = self.get_queryset().objects.get(pk=node_id)
            if not obj_server:
                return Response(error_result(code=1099, msg='申请删除的节点id错误', results='no'))
            is_owner_server = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=node_id))
            if not is_owner_server:
                return Response(error_result(code=1095, results='no', msg='你不是作者，不能创建'))
            obj_server.delete()
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 交换两个节点的次序
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def modify_order(self, request, *args, **kwargs):
        try:
            node_id1 = request.data['node_id']
            node_id2 = request.data['node_previous_id']
            is_owner_server = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=node_id1))
            if not is_owner_server:
                return Response(error_result(code=1095, results='no', msg='你不是作者，不能'))
            is_owner_server = self.get_queryset().objects.filter(Q(owner__id=request.user.id) & Q(id=node_id2))
            if not is_owner_server:
                return Response(error_result(code=1095, results='no', msg='你不是作者，不能创建'))
            obj_server1 = self.get_queryset().objects.filter(pk=node_id1)
            if not obj_server1:
                return Response(error_result(code=1099, msg='申请修改的节点id错误', results='no'))
            obj_server2 = self.get_queryset().objects.filter(pk=node_id2)
            if not obj_server2:
                return Response(error_result(code=1099, msg='申请修改的节点id错误', results='no'))
            obj_server1 = obj_server1.first()
            order1 = obj_server1.order
            obj_server2 = obj_server2.first()
            order2 = obj_server2.order
            if order1 == order2:
                order2 -= 1
            modify_dic1 = {'order': order2}
            modify_dic2 = {'order': order1}
            serializer = self.get_serializer(instance=obj_server1, data=modify_dic1, partial=True)
            if not serializer.is_valid():
                print(serializer.errors)
                return Response(error_result(code=1099, msg=data_error_text, results=serializer.errors))
            serializer.save()
            serializer = self.get_serializer(instance=obj_server2, data=modify_dic2, partial=True)
            if not serializer.is_valid():
                print(serializer.errors)
                return Response(error_result(code=1099, msg=data_error_text, results=serializer.errors))
            serializer.save()
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


class NodeCooperationViewSet(CustomNoQuestionsModelViewSet):
    queryset = NodeCooperation
    serializer_class = NodeCooperationSerializer

    # 新增协作
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_node_cooperation(self, request, *args, **kwargs):
        try:
            node_id = request.data['node']
            user_id = request.data['user']
            if request.user.id == user_id:
                return Response(error_result(code=1097, results='no', msg='这是作者，没必要邀请'))
            owner_server = Node.objects.filter(Q(id=node_id) & Q(owner__id=request.user.id))
            if not owner_server:
                return Response(error_result(code=1095, results={}, msg='你不是作者，无权邀请'))
            obj_server = self.get_queryset().objects.filter(Q(node__id=node_id) & Q(user__id=user_id))
            if obj_server:
                return Response(error_result(code=1098, results={}, msg='已经存在该邀请者'))
            cooperation_server = NodeCooperation.objects.filter(node__id=node_id)
            cooperation_count = 0
            # myVIP = request.user.VIP
            myVIP = User.objects.get(id=request.user.id).VIP
            can_invite_count = myVIP * 5
            if cooperation_server:
                cooperation_count = cooperation_server.count()
            if cooperation_count + 1 > can_invite_count and myVIP < 4:
                return Response(error_result(code=1096, results={}, msg='你的邀请能力已达极限，请升级VIP等级'))
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid(raise_exception=False):
                print(serializer.errors)
                return Response(error_result(code=1099, results='no', msg=serializer.errors))
            serializer.save()
            return Response(error_result(code=200, results='ok', msg='节点新增成功'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 取消课程协作
    @action(methods=['put'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def cancel_cooperation(self, request, pk):
        try:
            node_id = request.data['node_id']
            user_id = request.data['user_id']
            node_server = Node.objects.filter(Q(pk=node_id) & Q(owner__id=request.user.id))
            if not node_server:
                return Response(error_result(code=1098, msg='你无权操作该课程', results='no'))
            obj_server = self.get_queryset().objects.get(Q(node__id=node_id) & Q(user__id=user_id))
            if not obj_server:
                return Response(error_result(code=1099, msg='申请删除的节点id错误', results='no'))
            obj_server.delete()
            return Response(error_result(code=200, msg='取消协作成功', results='OK'))
        except Exception as E:
            print('Exception:', E)
            return Response(error_result(code=1099, msg='取消错题失败', results="", total=0))


class SeatworkViewSet(CustomNoQuestionsModelViewSet):
    queryset = Seatwork
    serializer_class = SeatworkModelSerializer

    # 新增课堂作业，参数为作业名称和试题id列表
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def create_work(self, request, *args, **kwargs):
        try:
            owner_id = self.request.user
            request.data['owner'] = owner_id
            question = request.data.pop('question')
            serializer = self.get_serializer(data=request.data)
            if not serializer.is_valid():
                print("序列化错误:", serializer.errors)
                return Response(error_result(code=406, msg=serializer.errors, results="data_error_text"))
            serializer.save()
            seatwork_obj = Seatwork.objects.get(id=serializer.id)
            seatwork_obj.question.add(*question)
            return Response(error_result(code=200, msg="恭喜，创建课堂作业成功！", results='ok'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 查询是否存在重名的作业
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def verify_same_name(self, request):
        try:
            work_name = request.query_params.get('work_name')
            obj_server_list = self.get_queryset().objects.filter(name=work_name)
            if len(obj_server_list) > 0:
                return Response(error_result(code=1099, msg="该作业已存在，请修改作业名", results="data_error_text"))
            return Response(error_result(code=200))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 教师布置作业给不同班级
class SetworkViewSet(CustomNoQuestionsModelViewSet):
    queryset = SetWork
    serializer_class = SetWorkModelSerializer
    pagination_class = MiniPage

    # 布置作业，参数为若干个班级的id
    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def set_work(self, request, *args, **kwargs):
        try:
            work_name = request.data['name']
            class_id_list = request.data['class_id_list']
            open_answer = request.data['open_answer']
            obj_server_list = Seatwork.objects.filter(name=work_name)
            if len(obj_server_list) > 0:
                return Response(error_result(code=1099, msg="该作业已存在，请修改作业名", results="data_error_text"))
            request.data['owner'] = request.user.id
            serializer_seatwork = SeatworkModelSerializer(data=request.data)
            if not serializer_seatwork.is_valid():
                print("Seatwork序列化错误:", serializer_seatwork.errors)
                return Response(error_result(code=406, msg=serializer_seatwork.errors, results=data_error_text))
            serializer_seatwork.save()
            # seatwork_obj = Seatwork.objects.get(id=serializer_seatwork.instance.id)
            setwork_list = []

            seatwork_obj = Seatwork.objects.get(id=serializer_seatwork.instance.id)
            teacher_obj = User.objects.get(id=request.user.id)
            setwork_dic = {'seatwork': seatwork_obj, 'teacher': teacher_obj,
                           'open_answer': open_answer, 'permit_write': True,
                           }
            setwork_obj = SetWork.objects.create(**setwork_dic)
            # setwork_obj = self.get_serializer(data=setwork_dic)
            # if not setwork_obj.is_valid():
            #     print("Setwork序列化错误:", setwork_obj.errors)
            #     return Response(error_result(code=406, msg=setwork_obj.errors, results=data_error_text))
            # setwork_obj.save()
            class_list = ClassGrade.objects.filter(id__in=class_id_list)
            setwork_obj_new = SetWork.objects.get(id=setwork_obj.id)
            setwork_obj_new.class_grade.add(*class_list)
            return Response(error_result(code=200, msg="恭喜，添加班级作业成功！", results='OK'))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 查询某个教师布置的所有作业列表
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_work_by_teacher(self, request):
        try:
            user_id = request.user.id
            # 得到该教师名下所有布置过的最近10条作业
            obj_server_list = self.get_queryset().objects.filter(teacher__id=user_id)
            if len(obj_server_list) == 0:
                return Response(error_result(code=1099, msg="该教师名下没有作业布置记录", results="data_error_text"))
            # 统计布置作业的所有班级的总人数，和已答题学生人数
            page = self.paginate_queryset(obj_server_list)
            serializer_list = self.get_serializer(page, many=True).data
            setwork_page_list = list(copy.deepcopy(serializer_list))
            for i in range(len(setwork_page_list)):
                sum_all = 0
                sum_done = 0
                work_page_item = setwork_page_list[i]
                setwork_id = work_page_item['id']
                class_list = work_page_item['class_list']
                for j in range(len(class_list)):
                    class_item = class_list[j]
                    class_item_id = class_item['id']
                    class_student_query = UserOfClass.objects.filter(Q(user__is_teacher=False) &
                                                                     Q(class_grade__id=class_item_id)).values('user')
                    class_student_id_list = convert_dic_list(dict_list=class_student_query, key='user')
                    class_work_record_query = WorkRecord.objects.filter(Q(user__id__in=class_student_id_list) &
                                                                        Q(setwork__id=setwork_id))
                    class_work_record_list = WorkRecordModelSerializer(class_work_record_query, many=True).data
                    class_done = len(class_work_record_query)
                    sum_done += class_done
                    # 按照id排序，两者按照和对比
                    setwork_page_list[i]['class_list'][j]['student_list'] = list(copy.deepcopy(setwork_page_list[i]['class_list'][j]['student_list']))
                    setwork_page_list[i]['class_list'][j]['student_list'].sort(key=lambda x: x['id'])
                    student_list = setwork_page_list[i]['class_list'][j]['student_list']
                    class_all = len(student_list)
                    sum_all += class_all
                    setwork_page_list[i]['class_list'][j]['sum_done'] = class_done
                    setwork_page_list[i]['class_list'][j]['sum_all'] = class_all
                    class_work_record_list = copy.deepcopy(list(class_work_record_list))
                    class_work_record_list.sort(key=lambda x: x['user_info']['id'])
                    index_record = 0
                    index_student = 0
                    while index_record < len(class_work_record_list) and index_student < len(student_list):
                        if class_work_record_list[index_record]['user_info']['id'] == student_list[index_student]['id']:
                            setwork_page_list[i]['class_list'][j]['student_list'][index_student]['work_record'] = copy.deepcopy(class_work_record_list[index_record])
                            index_student += 1
                            index_record += 1
                        elif class_work_record_list[index_record]['user_info']['id'] > student_list[index_student]['id']:
                            setwork_page_list[i]['class_list'][j]['student_list'][index_student]['work_record'] = {'score_rate': '未做'}
                            index_student += 1
                    while index_student < len(student_list):
                        setwork_page_list[i]['class_list'][j]['student_list'][index_student]['work_record'] = {'score_rate': '未做'}
                        index_student += 1
                setwork_page_list[i]['sum_all'] = sum_all
                setwork_page_list[i]['sum_done'] = sum_done
            return Response(error_result(code=200, results=setwork_page_list, total=len(obj_server_list)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))


# 学生提交作业，生成作业记录，参数为作业布置id和学生答题记录项列表
class WorkRecordViewSet(CustomNoQuestionsModelViewSet):
    queryset = WorkRecord
    serializer_class = WorkRecordModelSerializer
    pagination_class = MiniPage

    @action(methods=['post'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def set_work(self, request, *args, **kwargs):
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                obj = self.get_queryset().objects.filter(Q(user__id=self.request.user.id) &
                                                         Q(setwork__id=request.data['setwork']))
                if len(obj) > 0:
                    transaction.savepoint_rollback(sid)
                    return Response(error_result(code=1099, msg='老师还没给你布置作业', results="no"))
                setwork_id = request.data['setwork']
                setwork_obj_server = SetWork.objects.get(id=setwork_id)
                user_answer_list = request.data['user_answer_list']
                if len(user_answer_list) == 0:
                    transaction.savepoint_rollback(sid)
                    return Response(error_result(code=1098, msg='必须提交答题记录', results="no"))
                sum_score = sum(item['score'] for item in user_answer_list)
                score_rate = str(round(sum_score / len(user_answer_list) * 100, 2)) + '%'
                # 因为序列化中字段被占用，所以反序列化时比较麻烦
                workRecord_obj = WorkRecord(setwork=setwork_obj_server, user=request.user, score_rate=score_rate)
                workRecord_obj.save()
                for i in range(len(user_answer_list)):
                    del (user_answer_list[i]['score'])
                    user_answer_list[i]['work_record'] = workRecord_obj.id
                serializer_item = WorkRecordItemModelSerializer(data=user_answer_list, many=True)
                if not serializer_item.is_valid():
                    transaction.savepoint_rollback(sid)
                    print("WorkRecordItem序列化错误:", serializer_item.errors)
                    return Response(error_result(code=406, msg=serializer_item.errors, results=data_error_text))
                serializer_item.save()
                return Response(error_result(code=200, msg="恭喜，提交作业成功！", results='OK'))
            except Exception as e:
                print('Exception:', e)
                transaction.savepoint_rollback(sid)
                return Response(error_result(code=500))

    # 查询某个班级的所有学生的作业记录列表（跨表查询）
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_work_by_class(self, request):
        try:
            work_id = request.query_params.get('work_id')
            class_id = request.query_params.get('class_id')
            class_grade_obj = ClassGrade.objects.get(id=class_id)
            student_server_list = class_grade_obj.user.filter(is_teacher=False).values('id', 'username')
            student_list_id = convert_dic_list(dict_list=student_server_list, key='id')
            obj_server_list = self.get_queryset().objects.filter(
                Q(setwork__id=work_id) & Q(user__id__in=student_list_id))
            if len(obj_server_list) == 0:
                return Response(error_result(code=1099, msg="该教师名下没有作业布置记录", results="data_error_text"))
            # 查询没有交作业的学生
            obj_server_list = obj_server_list.values('id', 'setwork', 'user', 'add_time', 'score_rate')
            final_result = {'has_done': obj_server_list}
            undo_list = []
            for i in range(len(student_server_list)):
                item = student_server_list[i]
                flag = False
                for j in obj_server_list:
                    item_has = obj_server_list[j]['user']
                    if item['id'] == item_has['user']:
                        obj_server_list[j] = item
                        flag = True
                        break
                if not flag:
                    undo_list.append(item)
            final_result['undo'] = undo_list
            return Response(error_result(code=200, results=final_result, total=len(obj_server_list)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 查询某个id的作业记录列表
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_one_work_by_id(self, request):
        try:
            work_id = request.query_params.get('work_id')
            obj_server_obj = self.get_queryset().objects.filter(id=work_id)
            if len(obj_server_obj) == 0:
                return Response(error_result(code=1099, msg="没有作业布置记录", results="data_error_text"))
            serializer = self.get_serializer(obj_server_obj)
            return Response(error_result(code=200, results=serializer.data))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))

    # 查询某个学生所在班级的某个学科的所有作业记录列表, 并返回该学生哪些布置的作业已做和未做
    @action(methods=['get'],
            detail=False,
            authentication_classes=[Authentication],
            )
    def get_work_by_student(self, request):
        try:
            # subject_id = request.query_params.get('subject_id')
            student_id = request.user.id
            class_server_obj = UserOfClass.objects.filter(user__id=student_id).values('class_grade')
            class_id = class_server_obj[0]['class_grade']
            # 查询老师在该班级布置过的所有作业
            # 以下取values之后，seatwork只是取到外键id，并不是实体，因此需要序列化
            setwork_list_all_server = SetWork.objects.filter(Q(class_grade__id=class_id) & Q(permit_write=True))
            page = self.paginate_queryset(setwork_list_all_server)
            setwork_list_all = SetWorkForStudentModelSerializer(page, many=True).data
            setwork_list_all = copy.deepcopy(list(setwork_list_all))
            setwork_list_all.sort(key=lambda x: x['id'])
            # 查询该学生在做过的答题记录
            # work_record_student = WorkRecord.objects.filter(user__id=student_id).values('id', 'setwork',
            #                                                                             'user', 'score_rate')
            work_record_student_server = WorkRecord.objects.filter(user__id=student_id)
            work_record_student = WorkRecordModelSerializer(work_record_student_server, many=True).data
            work_record_student = copy.deepcopy(list(work_record_student))
            work_record_student.sort(key=lambda x: x['setwork_info']['id'])
            work_list = []
            done_index = 0
            all_index = 0
            while done_index < len(work_record_student) and all_index < len(setwork_list_all):
                if work_record_student[done_index]['setwork_info']['id'] == setwork_list_all[all_index]['id']:
                    new_item = copy.deepcopy(setwork_list_all[all_index])
                    new_item['score_rate'] = work_record_student[done_index]['score_rate']
                    new_item['WorkRecordItem'] = work_record_student[done_index]['WorkRecordItem']
                    work_list.append(new_item)
                    done_index += 1
                    all_index += 1
                elif int(work_record_student[done_index]['setwork_info']['id']) > int(setwork_list_all[all_index]['id']):
                    new_item = setwork_list_all[all_index]
                    new_item['score_rate'] = '未做'
                    work_list.append(new_item)
                    all_index += 1
                else:
                    done_index += 1
            while done_index < len(work_record_student):
                new_item = copy.deepcopy(setwork_list_all[all_index])
                new_item['score_rate'] = work_record_student[done_index]['score_rate']
                new_item['WorkRecordItem'] = work_record_student[done_index]['WorkRecordItem']
                work_list.append(new_item)
                done_index += 1
            while all_index < len(setwork_list_all):
                new_item = setwork_list_all[all_index]
                new_item['score_rate'] = '未做'
                work_list.append(new_item)
                all_index += 1
            # print('work_list of student:', work_list)
            return Response(error_result(code=200, results=work_list, total=len(setwork_list_all_server)))
        except Exception as e:
            print('Exception:', e)
            return Response(error_result(code=500))
